我很好奇是否存在
typename
本身不足以消除歧义的情况。
当使用 typename
消歧符时,后面的 qualified-id 必须是类型。
例如,在以下代码中:
template <typename T>
struct foo;
template <typename T, typename U>
void bar() {
typename foo<T>::mystery<0> x;
}
该标准要求使用
template
作为从属名称 mystery
,但如果您省略它,我不知道会出现解析歧义。 <0
不可能表示“小于零”,因为 typename
表示我们必须生成类型,而表达式永远不会生成类型。
GCC 编译了这个,但 clang 不 并产生错误:
error: use 'template' keyword to treat 'mystery' as a dependent template name
typename foo<T>::mystery<0> x;
^
template
我的理解是否正确,即
template
是标准所要求的,但对于编译器来说不是必需的?
C++23 中似乎对终端名称
mystery
有一些放宽,这可能允许在这里省略 typename
,但这些与 GCC 的实现无关。 C++98 模式下的 GCC 10 允许这样做:
template <typename T>
void bar() {
typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
}
我无法在变更日志中找到此功能。
即使任意嵌套,很明显
template
在这里也是不必要的,因为在我们解析 声明符 的 id-expression 之前,每个 <
都必须是模板尖括号。
简而言之,Clang 尚未实现所有放宽,而 GCC 过于宽松,允许您省略
template
。
P1787:声明以及在哪里可以找到它们。 大规模修改了围绕作用域、名称查找、依赖名称等的许多规则。
就目前情况而言,[temp.names] p3 指出:
A
被解释为 template-argument-list 的分隔符(如果它跟随的名称不是 conversion-function-id)并且<
- [...]
- 这是 using-declarator、declarator-id 中或 type-only context 中(而不是 nested-name-specifier)中的终端名称。
声明中:
typename foo<T>::mystery<0> x;
mystery
位于 typename-specifier 的 type-only context 中,因此 <0>
被解释为 template-argument-list,而不是 <
和 >
运营商。
此处可以安全地省略 template
关键字,但是,Clang 尚未实现此功能。
template
应该是强制的GCC 太过分了,允许:
typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
它不仅支持早在 C++23 生效之前的所有放宽,而且此声明也是无效的。
typename foo<T>::mystery<T>
可以解析为 类型名称说明符,其中 template
之前的 mystery
是可选的surprise<T>::
无效,因为 surprise
是 nested-name-specifier 中的终端名称,并且这些名称被排除在放宽范围之外(见上文)除了标准语言之外,GCC 直观上走得太远了,因为在
typename foo<T>::mystery<T>
之后,人们可以说“typename
消歧器已用完”。