您是否曾经需要将 typename 和 template 放在一起?

Do you ever need typename and template together?

提问人:Jan Schultke 提问时间:6/19/2023 最后编辑:Jan Schultke 更新时间:9/23/2023 访问量:213

问:

我很好奇是否存在一种情况,它本身并不能充分消除歧义。 使用消歧器时,以下 qualified-id 必须是类型。 例如,在以下代码中:typenametypename

template <typename T>
struct foo;

template <typename T, typename U>
void bar() {
    typename foo<T>::mystery<0> x;
}

该标准要求使用 of 作为依赖名称,但如果您省略它,我不知道解析歧义。不可能意味着“小于零”,因为说我们必须生成一个类型,而表达式永远不会产生类型。templatemystery<0typename

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 允许:mysterytypename

template <typename T>
void bar() {
    typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
}

我无法在更新日志中找到此功能。

即使使用任意嵌套,这里也显然没有必要,因为在我们解析声明符id-expression 之前,every 都必须是模板尖括号。template<

C++ 解析 语言-lawyer 依赖类型 C++23

评论

0赞 273K 6/19/2023
问得好。 在这种情况下不能解析为 less 运算符,因此为什么 clang 需要 : ?<templatetypename foo<T>::template mystery<0> x;
2赞 user17732522 6/19/2023
这与我们最近在您的另一个问题中讨论的问题相同:如果没有消除歧义,通常没有办法,或者至少没有简单的方法来判断嵌套名称的结束位置,因为可能是非类型模板参数中表达式中的小于运算符。在您的具体情况下,弄清楚这一点并不困难,但总的来说是有的。该标准根本没有为这些简单的情况做出任何特殊例外。template<
0赞 Jan Schultke 6/19/2023
@273K eel.is/c++draft/temp.names#3 是我认为的相关部分。该标准不考虑从外观上消除尖括号的歧义。typename
0赞 user17732522 6/19/2023
@JanSchultke 实际上,这是说您的特定示例不需要,因为: 由于引入,这是一个类型名称说明符,并且是该类型名称说明符的终端名称,因此它是一个纯类型的上下文,因此假定每个 eel.is/c++draft/temp.names#3.4 引入一个模板参数列表。在 C++23 中,这种情况发生了变化,open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.htmltemplatetypenametypename<
0赞 Jan Schultke 6/19/2023
终端名称@user17732522意味着这种放宽仅适用于姓氏?你需要吗?typename foo<T>::template mystery<0>::surprise

答:

0赞 Jan Schultke 9/23/2023 #1

简而言之,Clang 还没有实现所有放宽,而 GCC 在让你省略 . P1787 对许多关于范围、名称查找、从属名称等的规则进行了大规模修改:声明以及在哪里可以找到它们template

就目前情况而言,[temp.names] p3 指出:

如果 A 遵循的名称不是 conversion-function-id,则 A 被解释为 template-argument-list 的分隔符,并且<

在声明中:

typename foo<T>::mystery<0> x;

mysterytypename-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 是可选的templatemystery
  • surprise<T>::无效,因为是嵌套名称说明符中的终端名称,并且这些名称被排除在放宽之外(见上文)surprise

除了标准之外,GCC 在直觉上走得太远了,因为在之后,可以说“消歧器已经用完了”。typename foo<T>::mystery<T>typename