您是否需要同时使用类型名称和模板?

问题描述 投票:0回答:1

我很好奇是否存在

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 之前,每个
<
都必须是模板尖括号。

c++ parsing language-lawyer dependent-type c++23
1个回答
0
投票

简而言之,Clang 尚未实现所有放宽,而 GCC 过于宽松,允许您省略

template
P1787:声明以及在哪里可以找到它们

大规模修改了围绕作用域、名称查找、依赖名称等的许多规则。

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

A

<
被解释为 template-argument-list 的分隔符(如果它跟随的名称不是 conversion-function-id)并且

声明中:

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

mystery
位于 typename-specifiertype-only context 中,因此
<0>
被解释为 template-argument-list,而不是
<
> 
运营商。 此处可以安全地省略
template
关键字,但是,Clang 尚未实现此功能。

GCC 允许省略
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
消歧器已用完”。

© www.soinside.com 2019 - 2024. All rights reserved.