玩命加载中 . . .

16.4-可变参数模板


可变参数模板

一个可变参数模板就是一个接受可变数目参数的模板函数或模板类,可变数目的参数被称为参数包,存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数

template<typename T, typename... Args>
void foo(const T& t, const Args&... rest) {
    cout << sizeof...(rest) << endl;
}
// 打印参数包的参数个数
int i = 0;
double d = 3.14;
string s =  "hello";
foo(i, s, 42, d);   // 3
foo(s, 42, "hi");   // 2
foo(d, s);          // 1
foo("hi");          // 0

编译器会实例化出4个不同的版本

void foo(const int&, const string&, const int&, const double&);
void foo(const string&, const int&, const char[3]&);
void foo(const double&, const string&);
void foo(const char[3]&);

16.4.1 编写可变参数的函数模板

// 用来终止递归并打印最后一个元素
// 此函数必须在可变参数版本的print定义之前声明
template<typename T>
ostream& print(ostream& os, const T& t) {
    return os << t;
}

template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) {
    os << t << ", ";
    return print(os, rest...);
}

int i = 0;
string s =  "hello";
print(cout, i, s, 42);

一开始调用print(cout, i, s, 42)输出i
然后调用print(cout, s, 42)输出s
再调用print(cout, 42),此时两个版本都可以,但是非可变参数模板比可变参数模板更特例化,因此调用非可变参数版本,结束递归

16.4.2 包扩展

print函数包含两个扩展

template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) {   // 扩展Args
    os << t << ", ";
    return print(os, rest...);  // 扩展rest
}

第一个扩展操作扩展模板参数包,为print生成函数参数列表。第二个扩展操作出现在对print的调用中,此模式为print调用生成实参列表

递归调用的时候也可以对每个实参调用函数

template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) {   // 扩展Args
    os << t << ", ";
    return print(os, debug_rep(rest)...);  // 扩展rest
}

相当于

print(os, debug_rep(a1), debug_rep(a2), ..., debug_rep(an));

不能写成

print(os, debug_rep(rest...));

这相当于

print(os, debug_rep(a1, a2, ..., an));

16.4.3 转发扩展包

可变参数函数通常将它们的参数转发给其他函数

// fun有零个或多个参数,每个参数都是一个模板参数类型的右值引用
template<typename... Args>
void fun(Args&&... args) {  // 将Args扩展为一个右值引用的列表
    // work的实参既扩展Args,又扩展args
    work(std::forward<Args>(args)...);
}

int a = 10;
fun(a, 3.4);    // 可以接收左值,也可以接收右值

这里我们希望将fun的所有实参转发给另一个名为work的函数,假定由它完成函数的实际工作。work调用中的扩展既扩展了模板参数包也扩展了函数参数包

由于fun的参数是右值引用,因此我们可以传递给它任意类型的实参,由于使用std::forward传递这些实参,因此它们的所有类型信息在调用work时都会得到保持


文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录