可以將模板參數定義成能夠接受任意多個模板參數的情況。 這一類模板被稱為變參模板(variadic template)。
#include//為了結束遞歸, 重載了不接受參數的非模板函數 print(), 它會在參數包為空的時候被調用。
void print ()
{}
//這些被稱為 args的剩余參數, 是一個函數參數包(function parameter pack) :
templatevoid print (T firstArg, Types… args)
{std::cout<< firstArg<< '\n' ; //print first argument
print(args…); // call print() for remaining arguments
}
int main()
{std::string s("world");
print (7.5, "hello", s);
}
7.5
hello
World
解釋:
(7.5, “hello”, s);
其中:
firstArg 的值是 7.5, 其類型 T 是 double。
args 是一個可變模板參數, 它包含類型是 char const*的“hello” 和類型是 std::string 的“world”
(“hello”, s);
其中:
firstArg 的值是“hello” , 其類型 T 是 char const *。
args 是一個可變模板參數, 它包含的參數類型是 std::string。
其中:
firstArg 的值是“world” , 其類型 T 是 std::string。
args 是一個空的可變模板參數, 它沒有任何值
#include//為了結束遞歸, 重載了不接受參數的非模板函數 print(), 它會在參數包為空的時候被調用。
templatevoid print (T arg)
{std::cout<< arg<< '\n' ; //print passed argument
}
//這些被稱為 args的剩余參數, 是一個函數參數包(function parameter pack) :
templatevoid print (T firstArg, Types... args)
{std::cout<< firstArg<< '\n' ; //print first argument
print(args...); // call print() for remaining arguments
}
#includetemplatevoid print(T arg)
{(std::cout<< arg)<< '\n';
}
int main()
{std::cout<< "Hello World";
print();
return 0;
}
/home/insights/insights.cpp:10:5: error: no matching function for call to 'print'
print();
^~~~~
/home/insights/insights.cpp:3:6: note: candidate function template not viable: requires single argument 'arg', but no arguments were provided
void print(T arg)
^
1 error generated.
Error while processing /home/insights/insights.cpp.
3.sizeof… 運算符C++11 為變參模板引入了一種新的 sizeof 運算符: sizeof…。 它會被擴展成參數包中所包含的參數數目。
templatevoid print (T firstArg, Types… args)
{std::cout<< firstArg<< ’ \n’ ; //print first argument
std::cout<< sizeof…(Types)<< ’ \n’ ; //print number of remaining
types
std::cout<< sizeof…(args)<< ’ \n’ ; //print number of remainingargs
…
}
templatevoid print (T firstArg, Types… args)
{std::cout<< firstArg<< ’ \n’ ;
if (sizeof…(args) >0) {//error if sizeof…(args)==0
print(args…); // and no print() for no arguments declared
}
}
解釋:
但是這一方式是錯誤的, 因為通常函數模板中 if 語句的兩個分支都會被實例化。
因此如果在只有一個參數的時候調用 print()函數模板, 雖然 args…為空, if 語句中的 print(args…)也依然會被實例化, 但此時沒有定義不接受參數的 print()函數,因此會報錯
其他方法:如果使用 constexp if, 就可以在函數內部決定是否要繼續遞歸下去, 而不用再單獨定義一個函數來終結遞歸:
templatevoid print (T firstArg, Types… args)
{std::cout<< firstArg<< ’ \n’ ;
if constexpr(sizeof…(args) >0) {print(args…); //code only available if sizeof…(args)>0 (sinceC++17)
}
}
解釋:
從 C++17 開始, 提供了一種可以用來計算參數包(可以有初始值) 中所有參數運算結果的二元運算符。
templateauto foldSum (T… s) {return (… + s); // ((s1 + s2) + s3) …
}
更好的方法
templateauto sum(T ...t)
{return (0+...+t);
}
可能的折疊表達式:
eg:使用折疊表達式和運算符->*遍歷一條二叉樹的路徑:
// define binary tree structure and traverse helpers:
struct Node {int value;
Node* left;
Node* right;
Node(int i=0) : value(i), left(nullptr), right(nullptr) {}
};
auto left = &Node::left;
auto right = &Node::right;
// traverse tree, using fold expression:
templateNode* traverse (T np, TP… paths)
{//折疊表達式從 np 開始遍歷了 paths 中所有可變成員。
return (np ->* … ->* paths); // np ->* paths1 ->* paths2 …
}
int main()
{// init binary tree structure:
Node* root = new Node{0};
root->left = new Node{1};
root->left->right = new Node{2};
//traverse binary tree:
Node* node = traverse(root, left, right);
}
templateclass AddSpace
{private:
T const& ref; // refer to argument passed in constructor
public:
AddSpace(T const& r): ref(r) {}
friend std::ostream& operator<< (std::ostream& os, AddSpaces)
{return os<< s.ref<<’ ’ ; // output passed argument and a space
}
};
templatevoid print (Args… args) {(
std::cout<< …<< AddSpace(args) )<< ’ \n’ ;
}
//更好的方法
templatevoid print(T0 const &t0, T const &...t) {std::cout<< t0;
((std::cout<< ' '<< t), ...);
std::cout<< std::endl;
}
三、變參模板的使用一個重要的作用是轉發任意類型和數量的參數。
通常是使用移動語義對參數進行完美轉發(perfectly forwarded)
注意, 之前關于常規模板參數的規則同樣適用于變參模板參數。
// args are copies with decayed types:
templatevoid foo (Args… args);
// args are nondecayed references to passed objects:
templatevoid bar (Args const&… args);
四、變參類模板和變參表達式參數包還可以出現在其它一些地方, 比如表達式, 類模板, using 聲明, 甚至是推斷指引中。
1.變參表達式templatevoid printDoubled (T const&… args)
{print (args + args…);
}
如果這樣調用它:
printDoubled(7.5, std::string("hello"), std::complex(4,2));
效果上和下面的調用相同(除了構造函數方面的不同) :
print(7.5 + 7.5, std::string("hello") + std::string("hello"),
std::complex(4,2) + std::complex(4,2);
templatevoid addOne (T const&… args)
{print (args + 1…); // ERROR: 1… is a literal with too many decimal points
print (args + 1 …); // OK
print ((args + 1)…); // OK
}
templateconstexpr bool isHomogeneous (T1, TN…)
{return (std::is_same::value && …); // since C++17
}
對于:
isHomogeneous(43, -1, "hello")
會被擴展成:
std::is_same::value && std::is_same::value
結果自然是 false。
而對:isHomogeneous("hello", "", "world", "!")
結果則是 true, 因為所有的參數類型都被推斷為 char const *( 這里因為是按值傳遞, 所以
發生了類型退還, 否則類型將依次被推斷為: char const[6], char const[1], char const[6]和 char const[2]) 。
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
網站題目:C++模板(第二版)筆記之第四章:變參模板-創新互聯
網頁網址:http://vcdvsql.cn/article28/ddhjcp.html
成都網站建設公司_創新互聯,為您提供建站公司、靜態網站、關鍵詞優化、定制開發、網站策劃、小程序開發
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯