C++ variadic template
#include <vector> #include <iostream> #include <tuple> #include <type_traits> using namespace std; template <typename Arg> void process_recursion(Arg&& arg) { bool lb = std::is_rvalue_reference<decltype(arg)>::value; if (lb) { std::cout << "右值=" << arg << ","; } else { std::cout << "左值=" << arg << ","; } } template <typename First , typename... Rest> void process_recursion(First&& first, Rest&&... rest) { bool lb = std::is_rvalue_reference<decltype(first)>::value; if (lb) { std::cout << "右值=" << first << ","; } else { std::cout << "左值=" << first << ","; } process_recursion(rest...); } int main() { int a = 0; process_recursion(1,a); }
typename ... Args是变长参数包,Args... args是最简单的一种拆包形式。
对于函数模板 template < typename... Args > void test(Args... args);
当调用test(1,'F')时,依次进行实参推演,1被推演成int,‘F’被推演成char,则模板函数被具体化为void test(int ,char);
拆包还有更多的变化,对于template < typename... Args > void test(Args&&... args); 按照万能引用规则拆包。规则见《Effective Modern C++》条款1.
当调用int x=0; test(x,'F')时,依次进行实参推演,x被推演成int&,‘F’被推演成char&& , 则模板函数被具体化为void test(int& ,char&&);
1 template <typename... Types> void func1(std::vector<Types...> v1); 2 3 template <typename... Types> void func2(std::vector<Types>... v2);
对于func1, 只有1个参数v1,Types...拆包成vector类的两个模板参数(一个是所容纳的元素类型,另一个是分配器类型)
对于func2,可能有0个或任意多个参数。例如:func2( std::vector<int> , std::vector<int>, std::vector<char> );
由上可知,依次对多个可变模板参数处理,用到递归的方式处理比较繁琐,而且要注意递归的终结节点的确立。
下面给出一个迭代的方式处理多个模板参数。
template<typename T> void print_arg(T t) { std::cout << t << std::endl; } template<typename... Args> void print2(Args... args) { int tmp[] = { (printarg(args), 0)... }; } int main() { int x = 0; print2(1,x,7); }
逗号表达式,逗号分割的各个子表达式依次求值,因此能产生调用函数的效果。
解包后的伪代码为int tmp[] = { (printarg(1),0), (printarg(x),0),(printarg(7),0) };
为此方便的技巧,C++17特意的提供语言级别的comma fold expression
template<typename T> void print_arg(T&& t) { if constexpr (std::is_rvalue_reference<decltype(t)>::value) std::cout << "右值=" << t << std::endl; else std::cout << "左值=" << t << std::endl; } template<typename... Args> void print2(Args&&... args) { //int tmp[] = { (printarg(args), 0)... }; (print_arg(std::forward<Args>(args)), ...); } int main() { int x = 0; print2(1,x,7); }
此处,我们改进了一步,还额外提供完美转发能力
#include <iostream> using namespace std; template<typename Arg> void do_one(Arg arg) { std::cout << "," << arg ; } template<typename First, typename ...Rest> void values(First first, Rest... rest) { std::cout << "values("<< first; ( do_one(rest) , ... ); std::cout << ")" << std::endl; } int main() { values(1, 2, 3); }
应用:SQL语句的拼装,values子句就是大量不同类型,可变个数的数据拼接,使用C++17的新可变模板技术,SQL封装库将更方便。