如何编写支持可变数量参数对的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
相关产品推荐
相关产品推荐

