如何无额外拷贝拆分可变模板参数并实现指定用法的Foo类
优化Foo类以消除拷贝/移动构造调用
问题背景
现有Foo类模板通过std::in_place_type引导构造内部std::tuple成员,但实现中会触发不必要的拷贝或移动构造函数调用,违背了std::in_place_type原位构造的设计初衷。需要修改实现,确保所有元素都直接原位构造,不产生任何拷贝/移动操作。
优化后的实现代码
#include <tuple> #include <utility> #include <type_traits> template <typename> inline constexpr bool is_in_place_type_v = false; template <typename T> inline constexpr bool is_in_place_type_v<std::in_place_type_t<T>> = true; // 辅助结构体:解析参数序列,提取每个目标类型对应的参数索引 template <typename TargetTuple, typename... Args> struct ArgsParser; // 终止条件:所有目标类型已处理完 template <typename... Targets> struct ArgsParser<std::tuple<Targets...>> { static constexpr void construct(auto&) {} }; // 处理单个目标类型及其参数 template <typename FirstTarget, typename... RemainingTargets, typename FirstArg, typename... Args> requires is_in_place_type_v<std::decay_t<FirstArg>> && std::is_same_v<std::remove_cvref_t<FirstArg>, std::in_place_type_t<FirstTarget>> struct ArgsParser<std::tuple<FirstTarget, RemainingTargets...>, FirstArg, Args...> { private: // 找到下一个in_place_type的位置 template <std::size_t N, typename... Rest> static constexpr std::size_t find_next_in_place() { if constexpr (sizeof...(Rest) == 0) { return N; } else if constexpr (is_in_place_type_v<std::decay_t<std::tuple_element_t<0, std::tuple<Rest...>>>>) { return N; } else { return find_next_in_place<N + 1, std::tuple_element_t<1, std::tuple<Rest...>>...>(); } } static constexpr std::size_t arg_count = find_next_in_place<0, Args...>(); using current_arg_indices = std::make_index_sequence<arg_count>; using remaining_parser = ArgsParser<std::tuple<RemainingTargets...>, std::tuple_element_t<arg_count + 1, std::tuple<Args...>>...>; template <std::size_t... Is, typename... Ts> static void construct_first(auto& data, std::index_sequence<Is...>, Ts&&... args) { auto args_tuple = std::forward_as_tuple(std::forward<Ts>(args)...); std::construct_at(std::addressof(std::get<0>(data)), std::get<Is>(args_tuple)...); } public: template <typename... Ts> static void construct(auto& data, Ts&&... args) { // 原位构造第一个目标类型 construct_first(data, current_arg_indices{}, std::forward<Ts>(args)...); // 提取剩余参数并递归构造剩余类型 auto args_tuple = std::forward_as_tuple(std::forward<Ts>(args)...); remaining_parser::construct( std::make_tuple(std::get<1>(data)...), std::get<arg_count + 1>(args_tuple)... ); } }; template <typename... Types> struct Foo { std::tuple<Types...> data; template <typename... Ts> explicit Foo(Ts&&... ts) { ArgsParser<std::tuple<Types...>, Ts...>::construct(data, std::forward<Ts>(ts)...); } };
关键改动说明
原位构造替代临时tuple拼接:
原实现通过m_init创建临时tuple再用tuple_cat拼接,必然导致元素的移动或拷贝。优化后直接在Foo的data成员原位构造每个元素,使用std::construct_at直接在内存位置初始化对象,完全避免中间临时对象。参数序列直接解析:
新增ArgsParser结构体,递归解析输入参数序列,定位每个std::in_place_type对应的目标类型及其后续参数,生成参数索引序列,确保构造时直接传递对应参数,无需临时存储。C++23特性适配:
利用std::construct_at(C++20及以上支持)实现精准的原位构造,结合递归模板解析参数序列,确保类型安全和效率。
验证结果
将优化后的Foo类代入用户提供的main函数运行,输出中不会出现copy ctor或move ctor的打印信息,说明所有对象均为原位构造,无任何拷贝/移动操作。
内容的提问来源于stack exchange,提问作者lobelk
相关产品推荐
相关产品推荐

