c++11學習筆記(8)- 可變參數宏、函數、模板


1. 可變參數宏

可變參數的宏在C語言中就有定義,使用 ,在宏定義中表示可變參數;使用 VA_ARGS 替代宏定義中的可變參數。
比如我們想要定義 printf() 函數爲宏 PR,下面是一個簡單的例子:

// 宏定義PR爲printf函數
#ifndef PR
#define PR(pStr, ...) \
	printf(pStr, __VA_ARGS__);
#endif

int main(int argc, char** argv){
	PR("%d, %d\n", 10, 20)
}

函數的運行結果爲:
10,20

2. 可變參數函數

下面是一個求和的一個函數

int getSum(int count, ...){
	va_list ap;
	int sum = 0;
	va_start(ap, count);
	for (int i = 0; i < count; ++i)
		sum += va_arg(ap, int);
	va_end(ap);
	return sum;
}

int main(int argc, char** argv) {
	std::cout << getSum(3, 1, 2, 3) << std::endl;
	std::cout << getSum(5, 1, 2, 3, 4, 5) << std::endl;
}

上面程序運行結果爲:
6
15

這是一個求和函數,函數中count表示參數的個數;va_list 類型的ap用來輔助獲取參數。這裏首先使用函數 va_start() 對ap進行初始化,使ap變成了一個邊長參數的句柄;然後使用函數 va_arg() 將參數一一取出進行運算;最後使用函數 va_end() 結束操作。

3. 可變參數模板

使用可變參數模板時,在 typename 或者 class 後面加上三個點(…)表示可變參數模板。使用sizeof… 可以獲取參數的個數; 下面是一個簡單的例子

template<typename... T>
void f(T... args){
	std::cout << sizeof...(args) << std::endl;
}

int main(int argc, char** argv) {
	f(1, 2);
	f(1, 2, 3);
	f(1, 2, "aaa", 20.5);
}

程序的輸出結果爲
2
3
4

(1) 可變參數模板的展開 - 遞歸展開

關於可變參數模板的展開有兩種方式,一種是遞歸形式的展開;必須要定義一個終止函數,如下面的程序:

template<typename T>
void func(T t){	// 終止函數
	std::cout << t << std::endl;
}

template<typename T, typename... Args>
void func(T head, Args... args){
	std::cout << head << std::endl;
	func(args...);
}

int main(int argc, char** argv){
	func(1, 2, 3, 4, 5);
}

終止函數是爲了遞歸終止時使用的,上面的代碼展開爲:
func(1, 2, 3, 4, 5);
func(2, 3, 4, 5);
func(3, 4, 5);
func(4, 5);
當遞歸到func(4, 5)時,head傳遞4, 調用已經具化的函數func(5)。

可以簡單寫一個求和的模板函數:

template<typename T>
T sum(T t){
	return t;
}

template<typename T, typename... Args>
T sum(T head, Args... args){
	return head + sum(args...);
}

int main(int argc, char** argv){
	std::cout << sum(1, 2, 3, 4, 5) << std::endl;
}

上面的的程序運行結果爲:
15

(2) 可變參數模板的展開 - 非遞歸展開

可以使用初始化參數列表的形式展開函數模板, 首先看一下下面的代碼:

template<typename T>
void printTag(T t){
	std::cout << t << std::endl;
}

template<typename... Args>
void expand(Args... args){
	int array[] = { (printTag(args), 0)... };
}

int main(int argc, char** argv){
	expand(1, 2, 3, 4, 5);
}

上面的代碼展開爲:
int array[] = { (printTag(1), 0), (printTag(2), 0), (printTag(3), 0), (printTag(4), 0), (printTag(5), 0) };

同理也可以使用初始化參數列表的形式:

template<typename Func, typename... Args>
void expandFunc(const Func& f, Args... args){
	std::initializer_list<int>{(f(std::forward<Args>(args)), 0)...};
}

int main(int argc, char** argv) {
	expandFunc([](int i){std::cout << i << std::endl; }, 1, 2, 3);
}

這裏使用的 std::forward 實現完美轉發,關於完美轉發可參考這篇文章
c++11學習筆記(5)- 引用摺疊和完美轉發
lambda表達式傳遞Func,參數1,2,3分別調用該lambda表達式。

(3) 一個函數代理類的實例

我們要實現一個函數代理類,這個類的用法如下:

class TestFuncObject
{
public:
	int runFunc1(int a, int b){
		return a + b;
	}
	int runFunc2(int a, int b, int c){
		return a + b + c;
	}
}

int main(int argc, char** argv){
	TestFuncObject o;
	
	auto  f1 = createDelegate(&o, &TestFuncObject::runFunc1);
	std::cout << f1(10, 20) << std::endl;
	
	auto  f2 = createDelegate(&o, &TestFuncObject::runFunc2);
	std::cout << f2(10, 20, 30) << std::endl;
}

函數 createDelegate 創建一個代理類,然後直接使用該代理函數就可完成被代理函數。
函數的運行結果爲:
30
50

下面時代理函數和代理類的具體實現:

template<typename T, typename R, typename... Args>
class MyDelegate{
public:
	MyDelegate(T* t, R (T::*f)(Args...)) :m_t(t), m_f(f){}
	
	R operator()(Args... args){
		return (m_t->*m_f)(std::forward<Args>(args)...);
	}

private:
	T* m_t;
	R (T::*m_f)(Args...);
};

template<typename T, typename R, typename... Args>
MyDelegate<T, R, Args...> createDelegate(T* t, R(T::*f)(Args...)){
	return MyDelegate<T, R, Args...>(t, f);
}

MyDelegate 爲代理類,重載了(),爲一個仿函數。m_t爲對象指針,m_f爲該對象的函數指針。createDelegate 爲代理函數。


作者:douzhq
個人主頁:https://www.douzhq.cn
文章同步頁: https://douzhq.cn/c11_8/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章