为何我的C++模板特化编译失败?GetDataTypeOf匹配问题
问题:提取类成员函数返回类型的模板特化匹配失败
我编写了一段C++代码,尝试通过GetDataTypeOf模板提取MyRNG类的Rand()方法返回类型,代码如下:
#include <iostream> template<typename T> class MyRNG{ public: T Rand(){return T();}; }; template<typename RNG, typename = void> struct GetDataTypeOf; template<typename RNG> struct GetDataTypeOf<RNG, decltype(std::declval<RNG>().Rand())> { typedef decltype(std::declval<RNG>().Rand()) type; }; template<typename RNG> using DataTypeOf = typename GetDataTypeOf<RNG>::type; int main(){ std::cout << typeid(DataTypeOf<MyRNG<int>>).name() << std::endl; }
编译时触发如下错误:
incomplete type is not allowed using DataTypeOf = typename GetDataTypeOf<RNG>::type; ^ detected during instantiation of type "DataTypeOf<MyRNG<int>>" at line 22
经检查发现GetDataTypeOf<MyRNG<int>>未匹配到任何特化类定义,使用gcc 9.4.0编译器,请问这是什么原因?
原因分析
问题出在模板特化的参数匹配逻辑上:
- 主模板
GetDataTypeOf的第二个参数默认值是void,当你写GetDataTypeOf<MyRNG<int>>时,实际实例化的是GetDataTypeOf<MyRNG<int>, void>。 - 你的特化版本第二个参数是
decltype(std::declval<RNG>().Rand()),对于MyRNG<int>来说,这个类型是int,和主模板的默认参数void不匹配,因此该特化不会被编译器选中。 - 最终编译器只能使用未定义
type成员的主模板,导致“不完整类型”的编译错误。
解决方法
需要调整特化的第二个参数,让它在Rand()方法存在时类型等于void,从而匹配主模板的默认参数。可以借助逗号表达式结合void()实现SFINAE(替换失败不是错误)机制:
修改后的代码如下:
#include <iostream> #include <utility> // 引入std::declval template<typename T> class MyRNG{ public: T Rand(){return T();}; }; template<typename RNG, typename = void> struct GetDataTypeOf; template<typename RNG> struct GetDataTypeOf<RNG, decltype(std::declval<RNG>().Rand(), void())> { using type = decltype(std::declval<RNG>().Rand()); // 用using替代typedef更符合现代C++风格 }; template<typename RNG> using DataTypeOf = typename GetDataTypeOf<RNG>::type; int main(){ std::cout << typeid(DataTypeOf<MyRNG<int>>).name() << std::endl; }
关键修改说明
- 将特化的第二个参数改为
decltype(std::declval<RNG>().Rand(), void()):逗号表达式会先执行std::declval<RNG>().Rand()(仅做类型合法性检查,不会实际执行代码),然后返回void()的类型void,这样就和主模板的默认参数void完全匹配,特化会被正确选中。 - 当
RNG类不存在Rand()方法时,SFINAE机制会自动排除该特化,继续使用主模板(此时若尝试访问type仍会报错,符合预期的错误提示)。
内容的提问来源于stack exchange,提问作者lei hu
相关产品推荐
相关产品推荐

