C++20中编译器可否忽略依赖类型所需的typename关键字?
MSVC与GCC/Clang在
typename依赖类型处理上的差异及规范分析 有一段代码在MSVC下可正常编译,但因为依赖类型前缺失typename关键字,在GCC和Clang中会编译失败:
struct Foo { struct value{ }; }; struct Bar { int value; }; template<typename T> constexpr size_t SIZE = sizeof(T::value); // 针对Foo的场景,这里缺失typename constexpr size_t s1 = SIZE<Foo>; // 上面的依赖类型前未加typename constexpr size_t s2 = SIZE<Bar>;
MSVC不要求在sizeof中写typename的逻辑看似合理——毕竟sizeof既可以作用于类型,也可以作用于变量;但GCC和Clang严格遵循C规范,哪怕是C20标准下,当上下文无法明确区分T::value是类型还是变量时,必须显式添加typename。
核心疑问
MSVC这种宽松的处理方式,到底是符合C++20规范的行为,还是属于编译器缺陷?
再看一组涉及concept的代码,三款编译器的行为差异更明显:
template<typename T> concept A1 = sizeof(typename T::value) > 0; template<typename T> concept A2 = sizeof(T::value) > 0; struct Foo { struct value{ }; }; constexpr bool v1 = A1<Foo>; // 所有编译器均返回true constexpr bool v2 = A2<Foo>; // MSVC返回true,GCC和Clang返回false
GCC和Clang会将A2中省略typename的写法判定为非法,因此concept的判断结果为false;而MSVC则认可这种省略写法,判断结果为true。
规范依据分析
根据C++20规范的[temp.res.general]章节,并未将sizeof列入可以省略typename的场景,理论上必须显式添加typename来标记依赖类型,但规范也没有明确说明省略typename属于非法行为。
跨编译器兼容方案
要让代码在MSVC、GCC、Clang上都能正常编译运行,最稳妥的做法是始终显式添加typename标记依赖类型:
- 第一个例子中,将
sizeof(T::value)改为sizeof(typename T::value) - 第二个例子中,将A2的定义修改为和A1一致的写法
内容的提问来源于stack exchange,提问作者Amir Kirsh
相关产品推荐
相关产品推荐

