提问人:Jan Schultke 提问时间:6/19/2023 最后编辑:Jan Schultke 更新时间:9/23/2023 访问量:213
您是否曾经需要将 typename 和 template 放在一起?
Do you ever need typename and template together?
问:
我很好奇是否存在一种情况,它本身并不能充分消除歧义。
使用消歧器时,以下 qualified-id 必须是类型。
例如,在以下代码中:typename
typename
template <typename T>
struct foo;
template <typename T, typename U>
void bar() {
typename foo<T>::mystery<0> x;
}
该标准要求使用 of 作为依赖名称,但如果您省略它,我不知道解析歧义。不可能意味着“小于零”,因为说我们必须生成一个类型,而表达式永远不会产生类型。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 中有一些放宽,仅终端名称 ,这可能允许在此处省略,但这些与 GCC 的实现无关。C++98 模式下的 GCC 10 允许:mystery
typename
template <typename T>
void bar() {
typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
}
我无法在更新日志中找到此功能。
即使使用任意嵌套,这里也显然没有必要,因为在我们解析声明符的 id-expression 之前,every 都必须是模板尖括号。template
<
答:
简而言之,Clang 还没有实现所有放宽,而 GCC 在让你省略 .
P1787 对许多关于范围、名称查找、从属名称等的规则进行了大规模修改:声明以及在哪里可以找到它们。template
就目前情况而言,[temp.names] p3 指出:
如果 A 遵循的名称不是 conversion-function-id,则 A 被解释为 template-argument-list 的分隔符,并且
<
- [...]
- 这是 using 声明器、declarator-id 或嵌套名称说明符以外的纯类型上下文中的终端名称。
在声明中:
typename foo<T>::mystery<0> x;
mystery
在 typename-specifier 的纯类型上下文中,因此 被解释为 template-argument-list,而不是 as 和运算符。
这里可以安全地省略关键字,但是,Clang 尚未实现这一点。<0>
<
>
template
GCC 允许省略应该强制执行的地方template
GCC 通过允许:
typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
它不仅支持早在 C++23 生效之前的所有放宽,而且此声明也无效。
typename foo<T>::mystery<T>
可以解析为 typename-specifier,其中 before 是可选的template
mystery
surprise<T>::
无效,因为是嵌套名称说明符中的终端名称,并且这些名称被排除在放宽之外(见上文)surprise
除了标准之外,GCC 在直觉上走得太远了,因为在之后,可以说“消歧器已经用完了”。typename foo<T>::mystery<T>
typename
评论
<
template
typename foo<T>::template mystery<0> x;
template
<
typename
template
typename
typename
<
typename foo<T>::template mystery<0>::surprise