如何在C++20中从花括号初始化列表推导参数包实现模板简写?
Foo<{1,2,3},{4,5,6}>的简写形式 核心需求
已在FooDom命名空间实现合法的FooImp<Aux<int...>, Aux<int...>>(要求两个int...参数包长度相同),希望用Foo<{1,2,3},{4,5,6}>替代冗长的FooDom::FooImp<FooDom::Aux<1,2,3>, FooDom::Aux<4,5,6>>,且必须符合C++20标准。
已尝试方案的问题
using别名定义:
template<int ...u, int ...v> using Foo<{u...},{v...}> = FooDom::FooImp< FooDom::Aux<u...>, FooDom::Aux<v...> >;G12.2.0(
-std=c++20)编译报错:error: template parameters not deducible in partial specialization: note:'u' note:'v',本质是C标准不允许用花括号列表作为模板别名的模板参数形式。继承方式:
template<auto,auto> class Foo; template<int ...u, int ...v> class Foo<{u...},{v...}>: public FooDom::FooImp< FooDom::Aux<u...>, FooDom::Aux<v...> >{};auto{}不符合C++20标准,即使切换到-std=c++2b,仍会因模板参数无法推导报错——花括号列表不是可推导的模板实参类型。
合规解决方案
C++20中无法直接将花括号列表作为模板参数传递,但可以通过中间类型包装参数包实现类似简写效果,以下是两种可行方案:
方案1:借助std::integer_sequence
利用C++14引入的std::integer_sequence作为参数包载体,配合模板别名+宏简化调用:
#include <utility> namespace FooDom{ template<int...> struct Aux{}; template<typename,typename> class FooImp; }; // 主模板占位 template<typename Seq1, typename Seq2> using Foo = FooDom::FooImp<FooDom::Aux<>, FooDom::Aux<>>; // 针对整数序列的特化 template<int... U, int... V> using Foo<std::integer_sequence<int, U...>, std::integer_sequence<int, V...>> = FooDom::FooImp<FooDom::Aux<U...>, FooDom::Aux<V...>>; // 辅助宏进一步简化书写 #define FOO(SEQ1, SEQ2) Foo<std::integer_sequence<int, SEQ1>, std::integer_sequence<int, SEQ2>> // 使用示例 int main() { FOO(1,2,3, 4,5,6) foo; // 等价于FooDom::FooImp<FooDom::Aux<1,2,3>, FooDom::Aux<4,5,6>> }
该方案完全符合C++20标准,通过标准库类型包装参数包,宏则抹平了额外的语法层。
方案2:自定义参数包包装器+推导指引
自定义模板包装参数包,配合C++17类模板推导指引实现类对象的简写初始化:
namespace FooDom{ template<int...> struct Aux{}; template<typename,typename> class FooImp; }; // 自定义整数参数包包装器 template<int... Ts> struct IntPack {}; // Foo主模板 template<typename Pack1, typename Pack2> class Foo; // 特化版本,继承目标类 template<int... U, int... V> class Foo<IntPack<U...>, IntPack<V...>> : public FooDom::FooImp<FooDom::Aux<U...>, FooDom::Aux<V...>> {}; // 推导指引,支持通过IntPack对象推导模板参数 template<int... U, int... V> Foo(IntPack<U...>, IntPack<V...>) -> Foo<IntPack<U...>, IntPack<V...>>; // 使用示例 int main() { Foo foo{IntPack<1,2,3>{}, IntPack<4,5,6>{}}; // 等价目标类型 // 配合宏简化 #define MAKE_FOO(SEQ1, SEQ2) Foo(IntPack<SEQ1>{}, IntPack<SEQ2>{}) auto foo2 = MAKE_FOO(1,2,3, 4,5,6); }
此方案通过自定义模板包装参数包,推导指引让编译器能从初始化对象中推导模板参数,写法更贴近类的直观使用。
关键说明
C++标准中,花括号列表(braced-init-list)本身不是一个类型,无法直接作为模板实参传递。所有试图直接写Foo<{1,2,3},{4,5,6}>的写法都不符合标准,必须通过中间类型(如std::integer_sequence或自定义IntPack)包装参数包,才能实现合规的简写。
内容的提问来源于stack exchange,提问作者mhawth36

