如何让C++条件类模板非法实例化时触发编译错误?
如何让条件化C++类型别名在无匹配条件时触发编译错误?
此前我曾咨询过条件化创建C++类的实现,现在有如下代码。当所有条件都不满足时,我希望把代码里的
bool替换成一个“不存在的类”,这样在实例化该类时直接触发编译错误,请问这能实现吗?
示例代码:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstdint> #include <type_traits> // for std::conditional using namespace std; template <size_t N> using Float_4=conditional< 80<N && N<=128, _Float128,bool>::type; template <size_t N> using Float_3=conditional< 64<N && N<= 80,__float80 ,Float_4<N>>::type; template <size_t N> using Float_2=conditional< 32<N && N<= 64, _Float64 ,Float_3<N>>::type; template <size_t N> using Float_1=conditional< 16<N && N<= 32, _Float32 ,Float_2<N>>::type; template <size_t N> using Float_0=conditional< 0<N && N<= 16, _Float16 ,Float_1<N>>::type; typedef Float_0<80> Float;
当然可以实现,以下是几种实用方案:
方案1:用未定义类直接触发编译错误
你可以声明一个仅声明但不定义的类,把原代码中的bool替换成这个类。当所有条件都不满足时,编译器会因为找不到类的定义而报错:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstdint> #include <type_traits> using namespace std; // 仅声明,不提供类定义 class UnexistentClass; template <size_t N> using Float_4=conditional<(80<N && N<=128), _Float128, UnexistentClass>::type; template <size_t N> using Float_3=conditional<(64<N && N<= 80), __float80 ,Float_4<N>>::type; template <size_t N> using Float_2=conditional<(32<N && N<= 64), _Float64 ,Float_3<N>>::type; template <size_t N> using Float_1=conditional<(16<N && N<= 32), _Float32 ,Float_2<N>>::type; template <size_t N> using Float_0=conditional<(0<N && N<= 16), _Float16 ,Float_1<N>>::type; // 测试:当N=0时,会触发编译错误 // typedef Float_0<0> Float;
当实例化无匹配条件的类型时,编译器会输出类似invalid use of incomplete type 'class UnexistentClass'的错误,直接终止编译流程。
方案2:结合static_assert提供友好错误提示
如果希望错误信息更明确,便于排查问题,可以改用模板特化加static_assert的方式,自定义错误提示内容:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstdint> #include <type_traits> using namespace std; // 通用模板:当无匹配特化时触发断言 template <size_t N> struct FloatSelector { static_assert(N > 0 && N <= 128, "N must be in the range (0, 128]"); using type = void; // 占位类型,不会被实际使用 }; // 针对各个区间的特化(C++17及以上支持requires语法) template <size_t N> requires (0 < N && N <= 16) struct FloatSelector<N> { using type = _Float16; }; template <size_t N> requires (16 < N && N <= 32) struct FloatSelector<N> { using type = _Float32; }; template <size_t N> requires (32 < N && N <= 64) struct FloatSelector<N> { using type = _Float64; }; template <size_t N> requires (64 < N && N <= 80) struct FloatSelector<N> { using type = __float80; }; template <size_t N> requires (80 < N && N <= 128) struct FloatSelector<N> { using type = _Float128; }; // 定义类型别名 template <size_t N> using Float_0 = typename FloatSelector<N>::type; // 测试:N=0会触发自定义的断言错误 // typedef Float_0<0> Float;
这种方案的优势是错误信息直观,开发者能直接看到参数范围的要求,比未定义类的错误更易理解。
方案3:保持链式结构的SFINAE实现
如果你想保留原有的std::conditional链式写法,也可以用SFINAE(Substitution Failure Is Not An Error)机制,让无匹配条件的模板实例化失败:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstdint> #include <type_traits> using namespace std; // 基础模板:无匹配时触发断言 template <size_t N, typename = void> struct FloatChainedSelector { static_assert(N > 0 && N <= 128, "N is out of valid range"); }; // 逐个区间匹配 template <size_t N> struct FloatChainedSelector<N, enable_if_t<80 < N && N <= 128>> { using type = _Float128; }; template <size_t N> struct FloatChainedSelector<N, enable_if_t<64 < N && N <= 80>> { using type = __float80; }; template <size_t N> struct FloatChainedSelector<N, enable_if_t<32 < N && N <= 64>> { using type = _Float64; }; template <size_t N> struct FloatChainedSelector<N, enable_if_t<16 < N && N <= 32>> { using type = _Float32; }; template <size_t N> struct FloatChainedSelector<N, enable_if_t<0 < N && N <= 16>> { using type = _Float16; }; template <size_t N> using Float_0 = typename FloatChainedSelector<N>::type;
内容的提问来源于stack exchange,提问作者George Kourtis
相关产品推荐
相关产品推荐

