赞
踩
给你一个问题?
编写一个简单的
函数。
例如:
bool b = true;
print(12, ‘c’, 3.14, false, b, “hello”);
// output
// 12 c 3.14 false true hello
可变参数
:参数的数目是可变的,不固定的。即数目可以是任意有限个。
模板
:参数的类型是一种模板,是可经推导的,可以是任意存在的类型(系统类型或自定义类型)。
一个可变参数模板就是一个可以接受可变参数的模板函数或模板类。
形式如下:
template <typename... Args>
void func(Args... args) {}
template <class... Args>
class Obj {};
参数包
:可变数目的参数。
分析上面代码
template <typename... Args>
,定义了一个参数包,这包存放的可变参数的数据类型。类似于template <typename T>
,这是这里用typename...
代替了typename,用来说明定义的一个参数包(可以有多个类型数据)。Args
参数包名称,可更改,但我们常命名为 Args , 习惯所致。
void func(Args... args) {}
, 用上面定义的数据类型参数包Args,定义一个相对应的存放数据的数据包。Args... args
可以理解为对类型包 Args 依次取出数据类型,用来定义数据,最后把数据放入数据包 args 中。
注意:Args,args有的书上分别定义为模板参数包和函数参数包。 上面我说的Args是数据类型包,args是数据包,只是为了更好的理解Args和args。其实,我认为叫什么不重要,主要是你能更好的理解它。
int i = 2;
char c = 'A';
double d = 3.14;
func(i, c, d); // 包中有三个参数
func(i, c); // 包中有两个参数
func(i); // 有一个参数
func(); // 空包
编译器会为 func 分别实例化出四个不同的版本
void func(int, char, double);
void func(int, char);
void func(int);
void func();
到现在,我们已经学会简单的定义一个可变参数包。我们如何使用这个参数包的。
借助sizeof...
运算符,获得参数包中元素的个数。
template<typename... Args>
void putSize(Args... args)
{
cout << "sizeof...(Args) : " << sizeof...(Args) << endl; // 类型参数的数目
cout << "sizeof...(Args) : " << sizeof...(Args) << endl; // 数据参数的数目
}
更进一步,我们可以写个工具类,包装一下 sizeof…
#include <iostream> using namespace std; // 工具类,返回参数包中元素数目 template<class... Args> struct Count { static const std::size_t value() { return sizeof...(Args); } }; template<class... Args> void f(Args... args) { cout << sizeof...(Args) << endl; // 5 cout << sizeof...(args) << endl; // 5 cout << Count<Args...>::value() << endl; // 5 cout << Count<decltype(args)...>::value() << endl; // 5 } int main() { f(1, 2, 'c', 3.14, "hello"); return 0; }
C++不允许对通过[]
的方式进行解包, args[0] 是错误的。
C++的包展开是通过 args...
的形式,后面...
就意味着展开包。
template <typename T, typename... Args> void func(T v, Args... args)
{
func(args...); // 形如 func(arg1, arg2, arg3);
}
int i = 2; double d = 1.8; char c = 'c';
func(i, d, c); // Args 中包含三个类型数据 int、double、char // args 包含三个数据 2、1.8、'c'
上面是直接展开包,存进去什么,取出来什么。
但是如果现在有个需求,已知args 中存放的是 int i, double d, char c, 但我要求是展开后取出的数据都是数据对应的地址,即 &i, &d, &c,又该如何解包。
C++提供一种机制,可以实现。&args...
就可实现,每次取出的数据 arg 自动转换成 &arg。
验证一下。
#include <iostream> using namespace std; void p1(int a, int b) { cout << "a: " << a << " b: " << b << endl; } void p2(int *a, int *b) // 接受指针参数 { cout << "a: " << *a << " b: " << *b << endl; } template <class... Args> void f1(Args... args) { p1(args...); // ...代表解包 展开为 p1(arg1, arg2); p2(&args...); // &代表解包模式 &args... 被解包为 &arg1, &arg2, &arg3等等 // 展开为 p2(&arg1, arg2); } int main() { f1(2, 3); return 0; } /*output a: 2 b: 3 a: 2 b: 3 */
更进一步,我们可以这样解包。
f(&args...); // 展开成 f(&E1, &E2, &E3)
f(n, ++args...); // 展开成 f(n, ++E1, ++E2, ++E3);
f(++args..., n); // 展开成 f(++E1, ++E2, ++E3, n);
f(const_cast<const Args*>(&args)...); // f(const_cast<const E1*>(&X1), const_cast<const E2*>(&X2), const_cast<const E3*>(&X3))
f(h(args...) + args...); // 展开成 f(h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)
int res[size] = {1, args..., 2}; // 展开成 {1, arg1, arg2, arg3, 2}
Class c2 = Class(n, ++args...); // 调用 Class::Class(n, ++E1, ++E2, ++E3);
利用C++的模板推导来实现。
要求:
void f() { cout << endl; } // 如果 args 为空包, 即包已经全部展开,此时匹配此函数,结束递归。
template<typename T, typename... Args>
void f(T v, Args... args)
{
cout << v << ' ';
f(args...);
}
按照模板的推导规则,f 函数的调用规则是这样的。
依次从 args 参数包中取出一个参数放到 v 中, 剩下的依然放在包中。
void f<int, double, char>(int v, double __args1, char __args2);
void f<double, char>(double v, char __args1);
void f<char>(char v);
void f();
// Lambda 捕获
template<typename... Args>
void f2(Args... args)
{
auto lm = [&, args...] { return p1(args...); };
lm();
}
我们依然可以借助forward机制来编写函数。实现将参数原封不动的传递给其他函数。
注意:这是 std::forward 完美转发的知识。还包括左右值引用,万能引用,引用折叠等细节知识。不是我们文章的重点,我们就不展开说了。
一般右值引用的传递,我们使用std::forward<type>(value)
就行。对于参数包略有不同。借助std::forward<Args>(args)...
就可以实现参数的完美转发了。
template<typename T, typename... Args>
void f(T v, Args... args)
{
f(std::forward<type>(value)...);
}
要求: 编写一个简单的 print
函数,实现接受任意有限数量的参数,并输出。
#include <iostream> // 终止递归函数 void print(){ std::cout << std::endl; } // 递归解包函数 template <class T, class... Args> void print(T&& val, Args&&... args) { std::cout << std::boolalpha << val << " "; print(std::forward<Args>(args)...); // 转发参数包 } int main() { bool b = true; print(12, 'c', 3.14, false, b, "hello"); return 0; } /*output 12 c 3.14 false true hello */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。