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/