We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
模板参数接收任意数量的参数。
定义:
void print() {} template <typename T, typename... Types> void print(T firstArg, Types... args) { std::cout << firstArg << '\n'; // print first argument print(args...); // call print() for remaining arguments }
使用:
std::string s("world"); print (7.5, "hello", s);
C和GO都有类似的概念和定义方式,很好理解。定义void print() {}是为了终止递归。
void print() {}
args被叫做function parameter pack.
args
function parameter pack
返回parameter pack个数:
parameter pack
template<typename T, typename... Types> void print (T firstArg, Types... args) { std::cout << sizeof...(Types) << '\n'; // print number of remaining types ... }
也许有人会想利用sizeof...来判断:只有当可变参数模板的参数个数大于0时,才调用print,这样可以省略void print() {}:
sizeof...
print
template <typename T, typename... Types> void 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的所有代码都进行编译,而不会去根据if的条件去进行选择性的编译,选择运行if的哪个分支是在运行期间做的。
但是c++17引入了编译期的if(Compile-Time If),所以上面的代码可以这么写:
template <typename T, typename... Types> void print(T const &firstArg, Types const &... args) { std::cout << firstArg << '\n'; if constexpr (sizeof...(args) > 0) { print(args...); // code only available if sizeof...(args)>0 (since C++17) } }
if constexpr是c++17中编译期if的语法。这样就可以进行在编译期决定编译if条件的哪个分支。再举个例子:
if constexpr
template <typename T> std::string asString(T x) { if constexpr(std::is_same_v<T, std::string>) { return x; //如果T不是string就是无效的语句 } else if constexpr(std::is_arithmetic_v<T>) { return std::to_string(x); //如果x不是数字就是无效的语句 } else { return std::string(x); //如果不能转换为string就是无效的语句。 } }
从c++17开始,折叠表达式可以将二元运算符作用于所有parameter pack的参数上:
比如求parameter pack的和:
template<typename... T> auto foldSum (T... s) { return (... + s); // ((s1 + s2) + s3) ... }
再比如上面的print例子可以简写成:
template<typename... Types> void print (Types const&... args) { (std::cout << ... << args) << '\n'; }
如果想要在每个参数中间输出空格,可以配合lambda:
template <typename FirstType, typename... Args> void print(FirstType first, Args... args) { std::cout << first; auto printWhiteSpace = [](const auto arg) { std::cout << " " << arg; }; (..., printWhiteSpace(args)); } int main() { print("hello","world","zhangyachen"); }
其中, (..., printWhiteSpace(args));会被展开为:printWhiteSpace(arg1), printWhiteSpace(arg2), printWhiteSpace(arg3)这样的格式。
(..., printWhiteSpace(args));
printWhiteSpace(arg1), printWhiteSpace(arg2), printWhiteSpace(arg3)
比如将每个parameter pack的参数double:
template<typename... T> void printDoubled (T const&... args) { print (args + args...); } printDoubled(7.5, std::string("hello"), std::complex<float>(4,2));
上面的调用会展开为:
print(7.5 + 7.5, std::string("hello") + std::string("hello"), std::complex<float>(4,2) + std::complex<float>(4,2);
如果只是想加1,可以改为:
template<typename... T> void 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 }
还可以用在Compile-time Expression中,比如下面的函数会判断所有的参数类型是否一致:
Compile-time Expression
template<typename T1, typename... TN> constexpr bool isHomogeneous (T1, TN...) { return (std::is_same<T1,TN>::value && ...); // since C++17 } isHomogeneous(43, -1, "hello");
std::is_same<int,int>::value && std::is_same<int,char const*>::value // false
template<typename C, typename... Idx> void printElems (C const& coll, Idx... idx) { print (coll[idx]...); } std::vector<std::string> coll = {"good", "times", "say", "bye"}; printElems(coll,2,0,3);
最后的调用相当于:
print (coll[2], coll[0], coll[3]);
比如标准库的Tuple:
template<typename... Elements> class Tuple; Tuple<int, std::string, char> t; // t can hold integer, string, and character
namespace std { template <typename T, typename... U> array(T, U...) -> array<enable_if_t<(is_same_v<T, U> && ...), T>, (1 + sizeof...(U))>; } std::array a{42,45,77};
关键点:
enable_if_t
is_same_v<T, U> && ...
c++17的新特性,中文翻译应该叫:变长的using声明。C++17尝鲜:变长 using 声明这篇文章关于using的来龙去脉讲的很清楚,推荐大家看看。
一个更实际的例子:
class Customer { private: std::string name; public: Customer(std::string const &n) : name(n) {} std::string getName() const { return name; } }; struct CustomerEq { bool operator()(Customer const &c1, Customer const &c2) const { return c1.getName() == c2.getName(); } }; struct CustomerHash { std::size_t operator()(Customer const &c) const { return std::hash<std::string>()(c.getName()); } }; // define class that combines operator() for variadic base classes: template <typename... Bases> struct Overloader : Bases... { using Bases::operator()...; // OK since C++17 }; int main() { // combine hasher and equality for customers in one type: using CustomerOP = Overloader<CustomerHash, CustomerEq>; std::unordered_set<Customer, CustomerHash, CustomerEq> coll1; std::unordered_set<Customer, CustomerOP, CustomerOP> coll2; ... }
这里给unordered_set提供自定义的Hash和KeyEqual。
Hash
KeyEqual
关于可变参数模板的应用场景和各种使用技巧有很多,这里只列了5种大方向的应用场景,但是起码下次遇到看不懂的地方时,知道往哪个大方向去查,不至于一头雾水 :)
(完)
欢迎大家关注我的知乎账号:https://www.zhihu.com/people/zhangyachen
朋友们可以关注下我的公众号,获得最及时的更新:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
模板参数接收任意数量的参数。
定义与使用
定义:
使用:
C和GO都有类似的概念和定义方式,很好理解。定义
void print() {}
是为了终止递归。args
被叫做function parameter pack
.sizeof...
返回
parameter pack
个数:也许有人会想利用
sizeof...
来判断:只有当可变参数模板的参数个数大于0时,才调用print
,这样可以省略void print() {}
:但是这样是错误的,因为模板在编译阶段也会将if的所有代码都进行编译,而不会去根据if的条件去进行选择性的编译,选择运行if的哪个分支是在运行期间做的。
Compile-Time If
但是c++17引入了编译期的if(Compile-Time If),所以上面的代码可以这么写:
if constexpr
是c++17中编译期if的语法。这样就可以进行在编译期决定编译if条件的哪个分支。再举个例子:折叠表达式 Fold Expressions
从c++17开始,折叠表达式可以将二元运算符作用于所有
parameter pack
的参数上:比如求
parameter pack
的和:再比如上面的
print
例子可以简写成:如果想要在每个参数中间输出空格,可以配合lambda:
其中,
(..., printWhiteSpace(args));
会被展开为:printWhiteSpace(arg1), printWhiteSpace(arg2), printWhiteSpace(arg3)
这样的格式。其他场景
Variadic Expressions
比如将每个
parameter pack
的参数double:上面的调用会展开为:
如果只是想加1,可以改为:
还可以用在
Compile-time Expression
中,比如下面的函数会判断所有的参数类型是否一致:上面的调用会展开为:
Variadic Indices
最后的调用相当于:
Variadic Class Templates
比如标准库的Tuple:
Variadic Deduction Guides
关键点:
enable_if_t
控制是否启用该模板。 这个后面文章会讲到。is_same_v<T, U> && ...
判断数组元素类型是否相同,跟上面提到的例子用法一样。Variadic Base Classes and using
c++17的新特性,中文翻译应该叫:变长的using声明。C++17尝鲜:变长 using 声明这篇文章关于using的来龙去脉讲的很清楚,推荐大家看看。
一个更实际的例子:
这里给unordered_set提供自定义的
Hash
和KeyEqual
。关于可变参数模板的应用场景和各种使用技巧有很多,这里只列了5种大方向的应用场景,但是起码下次遇到看不懂的地方时,知道往哪个大方向去查,不至于一头雾水 :)
(完)
欢迎大家关注我的知乎账号:https://www.zhihu.com/people/zhangyachen
朋友们可以关注下我的公众号,获得最及时的更新:
The text was updated successfully, but these errors were encountered: