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

如何从无重叠类型子集的元组构造超集类型元组

实现固定返回类型的MyTuple构造函数(支持子集参数拼接)

问题描述

我有一个本质是std::tuple包装器的类MyTuple(适用于std::tuple的方案同样适用),想要编写构造函数/类似tuple_cat的工具,满足以下要求:

  • 可接收任意数量同模板类型的参数
  • 每个参数的模板参数是返回类型的子集
  • 返回类型固定,未传入的成员自动默认构造
  • 禁止参数间出现类型重叠,且保留原类型顺序

直接使用std::tuple_cat的问题在于返回类型由参数决定,无法满足固定返回类型的需求。我给出了示例代码框架(注释中的约束需通过requires子句实现):

template<typename... Ts>
// Ts约束为每个类型仅出现一次
struct MyTuple {
    template<typename... Tuples>
    // Tuples约束为均为MyTuple的实例化
    // 每个Tuples<Us...>约束为Us是Ts的严格子集
    // 任意两个Tuples<Us...>约束为Us无重叠
    // 使用const&简化代码,暂不考虑std::forward
    MyTuple(Tuples const&... args)
    {
        // 不知如何实现
        // 使用初始化列表更好,但更不知如何处理
    }

    std::tuple<Ts...> mTuple;
};

使用示例:

// A..E为可默认构造的不同类型
using SuperTuple = MyTuple<A, B, C, D, E>;
// 传入A、B、C、E,D默认构造
auto super = SuperTuple{MyTuple<A>{}, MyTuple<E,B>{}, MyTuple<C>{}};

请问这是否可行?


可行性结论与实现方案

完全可行,我们可以借助C++20的**概念(Concepts)**实现编译期约束,结合编译期类型遍历和std::get完成目标tuple的初始化。

1. 基础约束:确保MyTuple模板参数无重复

首先给MyTuple本身添加约束,保证模板参数Ts...中每个类型唯一:

#include <tuple>
#include <type_traits>
#include <utility>
#include <cassert>

// 辅助工具:判断类型列表是否存在重复
template <typename T, typename... Rest>
constexpr bool has_duplicate = (std::is_same_v<T, Rest> || ...);

template <typename... Ts>
constexpr bool all_unique = (!has_duplicate<Ts, Ts...> && ...);

// 约束MyTuple的模板参数无重复
template <typename... Ts>
requires all_unique<Ts...>
struct MyTuple {
    using types = std::tuple<Ts...>; // 暴露内部类型列表
    std::tuple<Ts...> mTuple;

    // 默认构造函数,用于子集MyTuple的创建
    MyTuple() = default;

2. 构造函数的编译期约束

接下来给构造函数添加requires约束,满足所有参数要求:

// 辅助概念:判断U是否属于类型列表Ts...
    template <typename U, typename... TsList>
    concept is_one_of = (std::is_same_v<U, TsList> || ...);

    // 辅助概念:判断Us...是TsList...的子集
    template <typename... Us, typename... TsList>
    concept is_subset = (is_one_of<Us, TsList...> && ...);

    // 辅助常量:判断多个MyTuple的类型无重叠
    template <typename... Tuples>
    constexpr bool no_overlap = true;

    template <typename... Us, typename... RestTuples>
    constexpr bool no_overlap<MyTuple<Us...>, RestTuples...> = 
        (!has_duplicate<Us, typename RestTuples::types::value_type...> && ...) 
        && no_overlap<RestTuples...>;

    // 核心构造函数
    template <typename... Tuples>
    requires (std::is_same_v<Tuples, MyTuple<typename Tuples::types::value_type...>> && ...) // 所有参数都是MyTuple实例
        && (is_subset<typename Tuples::types::value_type..., Ts...> && ...) // 每个参数是目标类型的子集
        && no_overlap<Tuples...> // 参数间无类型重叠
    MyTuple(const Tuples&... args) {
        // 遍历目标类型列表,逐个初始化成员
        auto init_tuple = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            mTuple = std::make_tuple(
                [&]() -> std::tuple_element_t<Is, std::tuple<Ts...>> {
                    std::tuple_element_t<Is, std::tuple<Ts...>> val{};
                    bool found = false;
                    // 检查每个传入的MyTuple是否包含当前类型
                    (([&]() {
                        using CurrentType = std::tuple_element_t<Is, std::tuple<Ts...>>;
                        using TupleUs = typename Tuples::types::value_type...;
                        if constexpr (is_one_of<CurrentType, TupleUs...>) {
                            val = std::get<CurrentType>(args.mTuple);
                            found = true;
                        }
                    }(), ...));
                    return found ? val : CurrentType{};
                }()...
            );
        };
        init_tuple(std::index_sequence_for<Ts...>{});
    }
};

3. 完整测试示例

// 定义测试类型
struct A { int a = 0; };
struct B { int b = 1; };
struct C { int c = 2; };
struct D { int d = 3; };
struct E { int e = 4; };

int main() {
    using SuperTuple = MyTuple<A, B, C, D, E>;
    // 传入不同子集的MyTuple,D将默认构造
    auto super = SuperTuple{
        MyTuple<A>{{10}}, 
        MyTuple<E,B>{{20}, {30}}, 
        MyTuple<C>{{40}}
    };

    // 验证初始化结果
    assert(std::get<A>(super.mTuple).a == 10);
    assert(std::get<B>(super.mTuple).b == 30);
    assert(std::get<C>(super.mTuple).c == 40);
    assert(std::get<D>(super.mTuple).d == 3); // 默认值
    assert(std::get<E>(super.mTuple).e == 20);

    return 0;
}

关键要点说明

  • 编译期校验:通过自定义概念和requires子句,在编译阶段就确保参数符合要求,避免运行时错误。
  • 类型遍历与初始化:利用std::index_sequence遍历目标MyTuple的所有类型,对每个类型检查是否存在于传入的子集中,存在则取值,否则默认构造。
  • 无重叠检查:通过递归的no_overlap常量,确保所有传入的子集类型没有重复,避免冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 07:02:02