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

如何实现可聚合初始化的可变参数模板存储不可移动类型?

存储不可移动/不可拷贝类型的可变参数数据结构方案

你遇到的核心问题是std::make_tuple会尝试将传入的临时对象移动或拷贝到tuple成员中,但你的No类型删除了拷贝和移动构造函数,导致操作失败。而手写的Handmade能正常初始化,是因为聚合初始化会直接在结构体的内存空间中原地构造每个成员,完全不需要移动或拷贝操作。

针对可变参数的需求,不同C++标准下有不同的可行方案:

方案一:C++17及以上——直接用std::tuple聚合初始化

C++17标准开始,std::tuple被重新定义为聚合类型,支持直接聚合初始化。这意味着你可以绕过std::make_tuple,直接指定tuple的类型并初始化,这样就能原地构造所有不可移动/不可拷贝的成员:

#include <tuple>

template<typename T>
struct No {
    No(T){}
    No(const No &) = delete;
    No(No &&) = delete;
};

template<typename T>
auto no() -> No<T> { return No<T>(T()); }

auto main() -> int
{
    // 直接构造tuple并聚合初始化,无需移动/拷贝
    auto tuple = std::tuple<No<int>, No<double>, No<char>>{no<int>(), no<double>(), no<char>()};
    return 0;
}

这种方式完全利用了C++17的聚合初始化特性,不需要额外实现自定义结构。

方案二:C11/C14——自定义可变参数聚合结构体

如果需要兼容更早的C++标准,可以手动实现一个支持聚合初始化的可变参数结构体模板,通过递归继承来容纳不同类型的成员:

1. 定义可变参数结构体

#include <type_traits>

template<typename... Ts>
struct MyTuple;

// 空tuple的特化
template<>
struct MyTuple<> {};

// 递归继承:每个特化包含当前类型的成员,并继承剩余类型的MyTuple
template<typename T, typename... Ts>
struct MyTuple<T, Ts...> : MyTuple<Ts...> {
    T value;
};

2. 实现成员访问的get函数

为了方便访问指定位置的成员,需要实现一个类似std::get的工具函数:

// 非const版本
template<std::size_t I, typename T, typename... Ts>
typename std::enable_if<I == 0, T&>::type
get(MyTuple<T, Ts...>& t) {
    return t.value;
}

template<std::size_t I, typename T, typename... Ts>
typename std::enable_if<I != 0, typename std::tuple_element<I-1, MyTuple<Ts...>>::type&>::type
get(MyTuple<T, Ts...>& t) {
    return get<I-1>(static_cast<MyTuple<Ts...>&>(t));
}

// const版本
template<std::size_t I, typename T, typename... Ts>
typename std::enable_if<I == 0, const T&>::type
get(const MyTuple<T, Ts...>& t) {
    return t.value;
}

template<std::size_t I, typename T, typename... Ts>
typename std::enable_if<I != 0, const typename std::tuple_element<I-1, MyTuple<Ts...>>::type&>::type
get(const MyTuple<T, Ts...>& t) {
    return get<I-1>(static_cast<const MyTuple<Ts...>&>(t));
}

3. 使用示例

template<typename T>
struct No {
    No(T){}
    No(const No &) = delete;
    No(No &&) = delete;
};

template<typename T>
auto no() -> No<T> { return No<T>(T()); }

auto main() -> int
{
    MyTuple<No<int>, No<double>, No<char>> my_tuple = {no<int>(), no<double>(), no<char>()};
    // 访问第一个成员
    auto& int_member = get<0>(my_tuple);
    return 0;
}

这个自定义的MyTuple支持聚合初始化,所有成员都会原地构造,完全不需要移动或拷贝操作,完美适配你的No类型。


内容的提问来源于stack exchange,提问作者slyx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 14:32:21