C++17中不修改callB签名,如何规避别名模板非类型参数推导限制?
问题背景
已知除C++20的类模板实参推导场景外,别名模板无法被推导,因此以下代码无法编译:
#include <iostream> template <bool Bv> struct A; template <> struct A<true> { void print() const { std::cout << "True" << std::endl; } }; template <> struct A<false> { void print() const { std::cout << "False" << std::endl; } }; enum class Btypes : int { cat, dog, wolf }; template <Btypes bt> using B = A<(bt == Btypes::dog) ? true : false>; template <Btypes bt> void callB(const B<bt>& b) { b.print(); } int main() { B<Btypes::dog> b; callB(b); // 错误:找不到匹配的callB函数 return 0; }
由于涉及的非类型模板参数取值有限,理论上可进行无歧义推导。请问在C++17标准下,不修改callB函数的定义/签名且保留别名模板B的前提下,是否存在方法规避别名模板无法被推导的限制,使上述代码合法?
解决方案
在C++17中,可以通过给callB添加针对A特化类型的重载函数,实现别名模板实例的转发推导,具体实现如下:
核心思路是:B<bt>本质是A<bool>的别名,因此传入B<bt>对象时,编译器会匹配到对应A类型的重载函数,再由重载函数显式调用原模板callB<bt>,绕开别名模板无法推导的限制。
完整代码:
#include <iostream> template <bool Bv> struct A; template <> struct A<true> { void print() const { std::cout << "True" << std::endl; } }; template <> struct A<false> { void print() const { std::cout << "False" << std::endl; } }; enum class Btypes : int { cat, dog, wolf }; template <Btypes bt> using B = A<(bt == Btypes::dog) ? true : false>; // 原callB保持不变 template <Btypes bt> void callB(const B<bt>& b) { b.print(); } // 重载:处理A<true>类型,唯一对应Btypes::dog void callB(const A<true>& b) { callB<Btypes::dog>(b); } // 重载:处理A<false>类型,可任选一个对应枚举值(因行为一致) void callB(const A<false>& b) { callB<Btypes::cat>(b); } int main() { B<Btypes::dog> b; callB(b); // 正常编译,输出True B<Btypes::cat> c; callB(c); // 正常编译,输出False B<Btypes::wolf> w; callB(w); // 正常编译,输出False return 0; }
注意事项
如果callB的实现逻辑依赖于bt的具体枚举值(而非仅A的类型),则A<false>对应的多个枚举值会产生歧义,此时需要额外给A添加标识信息(比如静态成员变量记录对应枚举值),再通过类型特征推导准确的bt。但在你的示例场景中,callB仅依赖A的行为,因此上述方法完全可行。
内容的提问来源于stack exchange,提问作者francesco
相关产品推荐
相关产品推荐

