使用enable_if作为模板参数时出现"incomplete type"错误的原因咨询(附最小可复现示例)
使用enable_if作为模板参数时出现"incomplete type"错误的原因咨询(附最小可复现示例)
我现在有一段代码,想要针对Bar或者Baz类型的模板特化版本去调用hithere函数,但用GCC编译的时候却报了这个错误:
error: incomplete type ‘foo
’ used in nested name specifier
我就纳闷了,这个模板针对Bar的实例化明明应该是没问题的,为什么会提示类型不完整呢?
兄弟,你这个问题我之前也踩过坑!本质上是模板实例化的时机和enable_if的生效逻辑没对上导致的。
咱们先拆解下为什么会报“不完全类型”:
- 当你把
enable_if直接写在类模板的模板参数列表里时,编译器在解析foo<Bar>这个类型名的时候,需要先确定模板参数的有效性,但此时和Bar相关的特化逻辑可能还没被编译器看到,或者enable_if的条件判断结果还没确定,导致编译器认为foo<Bar>是个未完全定义的类型。 - 举个典型的错误写法例子(应该就是你代码里的情况):
你看,当main里写#include <type_traits> struct Bar {}; struct Baz {}; template<typename T, typename = std::enable_if_t<std::is_same_v<T, Bar> || std::is_same_v<T, Baz>>> struct foo; template<> struct foo<Bar> { static void hithere() {} }; int main() { foo<Bar>::hithere(); // 这里就会报你说的那个错误 }foo<Bar>::hithere()时,编译器首先会去匹配主模板foo的参数,主模板里的enable_if条件虽然对于Bar是成立的,但问题是:编译器在处理主模板声明的时候,还没看到foo<Bar>的特化定义,它会先假设主模板是要被实例化的,但主模板本身是个未定义的模板(只有声明没有定义),所以就会认为foo<Bar>是不完全类型。
那怎么解决呢?给你两个常用的思路:
- 思路一:把
enable_if的条件移到类的成员里,而不是模板参数列表。比如:
这样只有当调用#include <type_traits> struct Bar {}; struct Baz {}; template<typename T> struct foo { template<typename U = T> static std::enable_if_t<std::is_same_v<U, Bar> || std::is_same_v<U, Baz>> hithere() {} }; int main() { foo<Bar>::hithere(); // 正常编译通过 }hithere的时候,才会去检查enable_if的条件,此时类模板foo<Bar>已经被正确实例化了。 - 思路二:如果一定要在模板参数里用
enable_if,那要换用非类型模板参数的写法:
这种写法的区别是,#include <type_traits> struct Bar {}; struct Baz {}; template<typename T, std::enable_if_t<std::is_same_v<T, Bar> || std::is_same_v<T, Baz>, int> = 0> struct foo; template<> struct foo<Bar> { static void hithere() {} }; int main() { foo<Bar>::hithere(); // 正常编译通过 }enable_if是作为非类型模板参数的默认值,编译器在解析foo<Bar>时,能正确推导出非类型参数的有效性,同时因为特化已经在main之前声明,所以不会再判定类型不完全。
总结一下:核心问题就是主模板的模板参数里的enable_if会让编译器在解析类型名时就去校验条件,但此时特化可能未被处理,或者主模板本身没有定义,导致类型被判定为不完全。调整enable_if的放置位置或者写法,就能轻松规避这个问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

