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

如何编写支持可变数量参数对的ScopeSet类CTAD推导指南?

实现支持可变参数对的ScopeSet推导指南

要支持任意数量的「变量-值」参数对,你可以借助C++17及以上的变参模板、元编程工具来自动推导ScopeSet的模板参数,无需手动编写多版本推导指南。以下是完整实现方案:

1. 完善ScopeSet类的可变参数模板结构

首先确保你的ScopeSet类本身是可变参数模板,用来容纳多个变量的类型:

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

template<typename... Ts>
class ScopeSet {
public:
    // 构造函数接受任意数量的「变量-值」对
    template<typename... Args>
    ScopeSet(Args&&... args) {
        // 用折叠表达式遍历每一对参数,保存原值并设置新值
        std::apply(
            [this](auto&&... pairs) {
                ((save_and_set(std::forward<decltype(pairs)>(pairs))), ...);
            },
            group_args(std::forward<Args>(args)...)
        );
    }

    ~ScopeSet() {
        // 用折叠表达式恢复所有变量的原值
        std::apply(
            [this](auto&&... originals) {
                ((originals.first = originals.second), ...);
            },
            originals_
        );
    }

private:
    // 存储每个变量的引用和原值:std::pair<T&, T>的tuple
    std::tuple<std::pair<Ts&, Ts>...> originals_;

    // 辅助函数:保存单个变量的原值并设置新值
    template<typename T, typename U>
    void save_and_set(std::pair<T&, U>&& pair) {
        auto& var = pair.first;
        auto val = std::forward<U>(pair.second);
        originals_ = std::tuple_cat(originals_, std::make_tuple(std::make_pair(var, var)));
        var = val;
    }

    // 辅助函数:将扁平化的参数分组为pair的tuple
    template<typename... Args>
    auto group_args(Args&&... args) {
        return group_args_impl(std::index_sequence_for<Args...>(), std::forward<Args>(args)...);
    }

    // 分组实现:按索引两两分组
    template<typename... Args, std::size_t... Is>
    auto group_args_impl(std::index_sequence<Is...>, Args&&... args) {
        return std::make_tuple(
            std::make_pair(std::get<2*Is>(std::forward_as_tuple(args...)), std::get<2*Is+1>(std::forward_as_tuple(args...)))...
        );
    }
};

2. 编写可变参数的推导指南

接下来需要编写推导指南,从构造函数的参数包中自动提取每一对变量的类型(去掉引用):

步骤1:编写元工具类提取成对参数的类型

// 辅助元工具:从参数包的tuple中提取每一对第一个参数的类型(去掉引用)
template<typename Tuple, std::size_t... Is>
struct ExtractFirstPairsHelper {
    using type = std::tuple<
        std::remove_reference_t<std::tuple_element_t<2*Is, Tuple>>...
    >;
};

template<typename Tuple>
struct ExtractFirstPairs {
    static_assert(std::tuple_size_v<Tuple> % 2 == 0, "ScopeSet requires even number of arguments (variable-value pairs)");
    using type = typename ExtractFirstPairsHelper<
        Tuple,
        std::make_index_sequence<std::tuple_size_v<Tuple>/2>
    >::type;
};

template<typename Tuple>
using ExtractFirstPairs_t = typename ExtractFirstPairs<Tuple>::type;

// 辅助元工具:将tuple的类型转换为ScopeSet的模板参数
template<typename Tuple>
struct TupleToScopeSet;

template<typename... Ts>
struct TupleToScopeSet<std::tuple<Ts...>> {
    using type = ScopeSet<Ts...>;
};

template<typename Tuple>
using TupleToScopeSet_t = typename TupleToScopeSet<Tuple>::type;

步骤2:最终的推导指南

// 可变参数推导指南:自动推导ScopeSet的模板参数
template<typename... Args>
ScopeSet(Args&&...) -> TupleToScopeSet_t<ExtractFirstPairs_t<std::tuple<Args...>>>;

3. 使用效果

现在你可以传入任意数量的「变量-值」对,编译器会自动推导ScopeSet的模板参数:

int i = 1;
double d = 2.;
float f = 3.f;
{
    auto _ = ScopeSet(i, 10, d, 20., f, 30.f);
    assert(i == 10);
    assert(d == 20.);
    assert(f == 30.f);
}
assert(i == 1);
assert(d == 2.);
assert(f == 3.f);

关键说明

  • 推导指南会自动检查参数个数是否为偶数,若传入奇数个参数,static_assert会直接抛出编译错误,提示需要成对参数。
  • 所有元工具类都基于C++17标准特性,确保兼容性。
  • 类内部的group_args函数会把扁平化的参数两两分组为std::pair,方便统一处理每个变量的保存和赋值逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 07:59:57