如何实现模板参数包展开的「复制」操作?
模板参数包展开的「复制」操作实现方法
嘿,关于模板参数包展开的「复制」操作,其实主要分两种常见场景:一种是在模板参数层面复制整个参数包或逐个元素,另一种是在运行时对参数包的元素执行复制操作。下面我给你详细讲讲每种场景的实现方式~
一、模板参数层面的参数包复制
这种场景是指我们要在模板类型/值的层面,生成重复的参数序列,比如把int, string变成int, string, int, string,或者每个元素重复两次变成int, int, string, string。
1. 复制整个参数包
如果只是想把整个参数包完整复制一遍,用C++17的折叠表达式就能很轻松实现。比如我们写一个函数,把传入的参数打印两次:
#include <iostream> template<typename... Args> void print_twice(Args&&... args) { // 第一次打印参数包 (std::cout << ... << args) << '\n'; // 第二次复制展开参数包再打印 (std::cout << ... << args) << '\n'; } int main() { print_twice(1, "hello", 3.14); // 输出: // 1hello3.14 // 1hello3.14 }
如果要在模板类型里复制参数包(比如生成包含两份相同类型的tuple),可以借助一个辅助的拼接模板:
#include <tuple> // 辅助函数,用于拼接两个tuple的参数包 template<typename... Args1, typename... Args2> auto concat_pack(std::tuple<Args1...>, std::tuple<Args2...>) -> std::tuple<Args1..., Args2...>; // 定义一个别名,把同一个参数包拼接两次 template<typename... Args> using doubled_tuple = decltype(concat_pack(std::tuple<Args...>{}, std::tuple<Args...>{})); // 使用示例 doubled_tuple<int, std::string> t = {1, "test", 2, "copy"}; // t的类型是std::tuple<int, std::string, int, std::string>
2. 逐个复制参数包元素
如果需要把参数包里的每个元素都重复N次(比如每个元素复制两次),可以用递归模板或者C++17的折叠表达式来实现:
运行时逐个复制到容器
用折叠表达式可以很简洁地把每个元素复制后插入容器:
#include <vector> template<typename T, typename... Args> std::vector<T> repeat_each_element(Args&&... args) { std::vector<T> vec; // 对每个参数执行两次push_back,实现复制 ((vec.push_back(args), vec.push_back(args)), ...); return vec; } // 使用示例 auto vec = repeat_each_element<int>(1, 2, 3); // vec的内容是 [1, 1, 2, 2, 3, 3]
模板类型层面逐个重复
如果要在类型层面实现每个元素重复,需要用递归模板来展开:
#include <tuple> // 辅助模板:向tuple头部添加元素 template<typename... Args, typename... Ts> auto push_front(std::tuple<Args...>, Ts... ts) -> std::tuple<Ts..., Args...>; template<typename... Args> struct tuple_helper { template<typename... Ts> using push_front = decltype(::push_front(std::tuple<Args...>{}, std::declval<Ts>()...)); }; // 递归模板:逐个重复参数包元素 template<typename... Args> struct repeat_each; // 空参数包的基础情况 template<> struct repeat_each<> { using type = tuple_helper<>; }; // 递归展开:对当前参数重复两次,再处理剩余参数 template<typename T, typename... Rest> struct repeat_each<T, Rest...> { using type = typename repeat_each<Rest...>::type::template push_front<T, T>; }; // 使用示例 using repeated_tuple = repeat_each<int, double>::type; // repeated_tuple 的类型是 std::tuple<int, int, double, double>
二、运行时对参数包元素的复制操作
这种场景是指我们要对参数包里的每个元素执行复制动作(比如拷贝构造、复制到容器等)。
1. 把参数包元素复制到容器
直接用初始化列表就能把参数包的元素复制到vector这类容器里:
#include <vector> template<typename... Args> std::vector<typename std::common_type<Args...>::type> copy_to_vector(Args&&... args) { // 这里会根据参数的类型,执行值拷贝或移动构造 return {std::forward<Args>(args)...}; } // 使用示例 auto vec = copy_to_vector(1, 2, 3, 4); // vec 是包含 [1,2,3,4] 的 vector<int>
2. 对每个元素执行自定义复制操作
比如我们想复制每个元素,然后打印复制后的结果,可以结合折叠表达式和自定义函数:
#include <iostream> #include <string> // 复制单个元素并打印 template<typename T> void copy_and_print(const T& obj) { T copied_obj = obj; // 执行复制操作 std::cout << "复制后的值:" << copied_obj << '\n'; } // 展开参数包,对每个元素执行复制操作 template<typename... Args> void process_copies(Args&&... args) { (copy_and_print(std::forward<Args>(args)), ...); } // 使用示例 process_copies(1, std::string("hello"), 3.14); // 输出: // 复制后的值:1 // 复制后的值:hello // 复制后的值:3.14
内容的提问来源于stack exchange,提问作者Vahagn
相关产品推荐
相关产品推荐

