文章目录
- 定义
- 常见应用
- 模板参数包展开
- 可变参数模板类
- 可变参数模板函数重载
- 练习
- 打印任意数量的参数
- 将任意数量的参数合并为一个字符串
- 解包元组(Tuple Unpacking)
- emplace_back
- 和 push_back 的比较
- 实例
定义
可变参数模板(Variable-length argument template)是C++11引入的特性之一,也称为“展开包”(Expansion Pack)。它允许我们在函数模板中使用可变数量的参数,这些 参数可以是任意类型 ,并且可以在函数模板中以类似于函数参数列表的方式进行使用。
使用可变参数模板时,需要在函数模板的参数列表中 使用省略号(…) 来表示可变数量的参数,并通过模板参数包展开来访问这些参数。
它里面包含了 0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数
常见应用
模板参数包展开
可变参数的 模板参数包可以通过展开 来实现模板的多态性。
- 递归展开
// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value <<" ";ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("show"));return 0;
}
ShowList() 函数被定义为两个版本:一个递归终止函数和一个可变参数模板函数。递归终止函数只接受一个参数,并将它打印到标准输出中。可变参数模板函数接受一个参数和一个参数包,将第一个参数打印到标准输出中,并递归调用自身来处理参数包。
- 逗号表达式展开
template <class T>
void PrintArg(T t)
{// 打印模板参数cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{// 将可变参数展开成一个数组aint arr[] = { (PrintArg(args), 0)... };cout << endl;
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}
两次执行结果:
可变参数模板类
可变参数模板也可以用于定义类模板,如下图:
在这个例子中,我们定义了一个可变参数模板类 Tuple,它接受任意数量和类型的参数,并包含一个 std::tuple 类型的成员变量 values。在 main() 函数中,我们创建了一个包含整数、字符串和浮点数的元组,并将它赋值给 t 中的 values 成员变量。
可变参数模板函数重载
可变参数模板可以与函数重载一起使用,实现不同参数数量或类型的函数重载。例如:
template<class T>
void print(T t) {std::cout << t << std::endl;
}template<typename T, typename... Args>
void print(T t, Args... args) {std::cout << t << " ";print(args...);
}int main() {print(1, 2, 3, "hello", 'A'); // 输出 1 2 3 hello Areturn 0;
}
在这个例子中,我们定义了两个 print() 函数,一个接受单个参数,另一个接受任意数量的参数。在调用时,编译器会根据参数数量和类型来选择对应的函数进行调用。
练习
打印任意数量的参数
使用可变参数模板可以方便地打印任意数量的参数。例如,我们可以编写一个函数模板来打印任意数量的参数:
将任意数量的参数合并为一个字符串
使用可变参数模板可以方便地将任意数量的参数合并为一个字符串。例如,我们可以编写一个函数模板来将任意数量的字符串合并为一个字符串:
template<class ...Args>
std::string concat(Args... args) {// 创建一个stringstream对象ss,用于将多个参数拼接成一个字符串。std::stringstream ss;int arr[] = { (ss << args, 0)... }; // 折叠表达式,将所有参数写入 ss 中return ss.str();
}int main()
{// 执行下面代码string s = concat("hello", " ", "world", ".");cout << s << endl;return 0;
}
代码执行结果:(合并成功)
解包元组(Tuple Unpacking)
使用可变参数模板可以方便地解包元组(Tuple)。例如,我们可以编写一个函数模板来计算元组中所有元素的和:
// 解包元组(Tuple Unpacking)
template<class ...Args>
// 定义元组类
class Tuple
{
public:std::tuple<Args...> values;
};int main() {Tuple<int, std::string, double> tp;tp.values = std::make_tuple(1, "hello", 1.14);std::cout << std::get<0>(tp.values) << " " << std::get<1>(tp.values) << " " << std::get<2>(tp.values) << std::endl;return 0;
}
程序执行结果:
emplace_back
C++11 中 STL容器引入了 emplace
相关接口函数,它支持模板的可变参数,和 万能引用。
和 push_back 的比较
emplace_back()和push_back()都是向容器的尾部添加元素的函数,比如在C++中常用的std::vector容器。它们之间的主要区别在于元素的构造方式和性能方面。
- 构造方式
- push_back()函数接受一个已经构造好的元素作为参数,然后将该元素的副本插入到容器中。这意味着在调用push_back()之前,必须先创建一个完整的元素对象,然后将其复制。
- emplace_back()函数接受参数,并在容器内直接构造一个新的元素。它使用传递的参数来构造元素,而不是通过复制或移动现有的元素。因此,在调用emplace_back()时,可以直接传递构造该元素所需的参数,而无需提前创建完整的元素对象。
- 性能:
- emplace_back()函数接受参数,并在容器内直接构造一个新的元素。它使用传递的参数来构造元素,而不是通过复制或移动现有的元素。因此,在调用emplace_back()时,可以直接传递构造该元素所需的参数,而无需提前创建完整的元素对象。
- 相比之下,emplace_back()直接在容器内构造元素,并避免了额外的拷贝操作。这在大量添加元素时可以带来性能上的改进,尤其是对于复制构造代价较高的对象或容器。
实例
可以看出在使用上两者差别不大,主要是内部的具体过程