You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无额外拷贝拆分可变模板参数并实现指定用法的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:27:08