非C++17环境下,如何实现模板类B仅需单个模板参数引用模板类A?
针对你的问题——在不支持C17的编译器(比如你的arm-none-eabi-gcc)上,定义一个只需要接收A的引用/指针作为单一模板参数的模板类B,我来分享几个可行的C11/C++14兼容方案:
首先明确你的场景:
你有一个依赖整数模板参数的类A:
template <int n> class A{ /* 自定义代码 */ };
目前你能用的写法需要同时传入n和A的引用,比较繁琐:
template <int n, A<n> &a> class B{ /* 自定义代码 */ };
而C17的auto非类型模板参数虽然能简化成template <auto &a> class B,但你的编译器不支持。那我们可以用以下几种方法绕开C17的限制:
方案1:用工厂函数自动推导模板参数
这是最简洁的用法,不需要用户手动指定任何模板参数,完全由编译器推导:
首先定义模板类B,保留两个模板参数(类型T和T的引用a):
template <typename T, T& a> class B { public: // 利用内部模板提取A的n值 static constexpr int n = [](){ struct extract_n { template <int k> static constexpr int get(A<k>) { return k; } }; return extract_n::get(a); }(); // 这里写你的B类逻辑,比如使用a和n void do_something() { // 示例操作,可根据需求修改 // std::cout << "A's template parameter n = " << n << std::endl; } };
然后写一个工厂函数make_B,用于自动推导模板参数:
template <int n> B<A<n>, A<n>&> make_B(A<n>& a) { return B<A<n>, a>(); }
使用方式
// 定义A的实例 A<5> my_a; // 自动推导生成B的实例,无需手动写模板参数 auto my_b = make_B(my_a); my_b.do_something(); // 正常调用B的成员函数
这种方法的好处是用户完全不用关心模板参数的细节,用法和普通函数一样直观。
方案2:用宏简化模板参数输入
如果不想用工厂函数,也可以用宏来自动生成decltype(a)作为模板参数,避免手动输入A
首先定义B类:
template <typename T, T& a> class B { // 提取A的n值的辅助函数 template <int k> static constexpr int get_n(A<k>) { return k; } public: static constexpr int n = get_n(a); // 你的类逻辑 };
然后定义一个宏:
#define CREATE_B(a) B<decltype(a), a>
使用方式
A<7> another_a; CREATE_B(another_a) my_b; // 等价于B<A<7>, another_a> my_b;
宏会自动推导a的类型,你只需要传入实例名即可,省去了手动写A<7>的麻烦。
方案3:用辅助模板封装类型提取
如果你希望更“模板化”的解决方案,可以写一个辅助模板来提取A的模板参数n,然后在B类中直接使用:
// 辅助模板:从A<n>类型中提取n template <typename T> struct A_template_parameter; template <int n> struct A_template_parameter<A<n>> { static constexpr int value = n; }; // 定义B类 template <typename T, T& a> class B { public: static constexpr int n = A_template_parameter<T>::value; // 你的类逻辑 };
使用时可以结合方案1的工厂函数或者方案2的宏来简化输入,让B类内部能直接用n获取A的模板参数值。
需要注意的是,C17之前的非类型模板参数确实要求明确指定类型,所以无法完全像C17那样只写template <auto& a>,但上面的几种方法都能达到“用户只需传入A的实例,无需手动指定n”的效果,完美适配你的arm-none-eabi-gcc环境。
内容的提问来源于stack exchange,提问作者Alexander Daum

