如何避免用可变参数模板初始化vector时的不必要拷贝?
为什么会触发不必要的拷贝?
1. create_dummies函数的问题
你用初始化列表{std::forward<Dummies>(value)...}构造vector时,初始化列表的类型是std::initializer_list<Dummy>,而std::initializer_list的元素是**const T&**类型。这意味着即使传入的是右值(比如Dummy(1)),初始化列表也会将其视为const左值,无法调用Dummy的移动构造函数,只能触发拷贝构造。
2. create_elements函数的问题
std::make_any<Comps>(std::forward<Comps>(value))本身是完美转发构造内部对象的,但问题出在vector的初始化列表构造上:初始化列表里的std::any临时对象会被拷贝到vector的元素中,而std::any的拷贝构造会深度拷贝其内部存储的Dummy对象,因此触发了Dummy的拷贝构造。
修复方案
1. 修复create_dummies函数
放弃初始化列表,改用emplace_back直接在vector内存中构造元素,完全避免拷贝:
template<typename... Dummies> std::vector<Dummy> create_dummies(Dummies&&... values) { std::vector<Dummy> vec; vec.reserve(sizeof...(values)); // 预先分配空间,避免扩容带来的额外移动 (vec.emplace_back(std::forward<Dummies>(values)), ...); // 折叠表达式逐个构造 return vec; }
emplace_back会直接在vector的内存区域构造Dummy,传入右值时调用移动构造,传入参数时直接调用对应的构造函数,全程无拷贝。
2. 修复create_elements函数
同样避免初始化列表,用emplace_back结合std::in_place_type直接在std::any内部构造对象,同时避免std::any的拷贝:
template<typename... Comps> std::vector<std::any> create_elements(Comps&&... values) { std::vector<std::any> vec; vec.reserve(sizeof...(values)); (vec.emplace_back(std::in_place_type_t<std::decay_t<Comps>>{}, std::forward<Comps>(values)), ...); return vec; }
std::in_place_type确保std::any直接在自身内存中构造目标对象,emplace_back则直接在vector内存中构造std::any,全程无拷贝操作。std::decay_t<Comps>用于处理左值引用参数的情况,确保构造的是值类型而非引用类型。
修复效果
修改后的代码运行后,不会输出任何copy dummy的日志,和emplace_back方案的效果完全一致。
内容的提问来源于stack exchange,提问作者undersilence

