在依赖模板名称之前使用模板关键字

8
考虑以下代码示例,我本以为在这里需要使用template关键字来指导编译器将变量v视为模板。然而,MSVC拒绝使用template关键字,而Clang和GCC实际上要求使用它。C++20标准中的哪个具体规则要求或禁止在这种情况下使用template关键字?
struct s {
    template<typename...>
    static constexpr auto v = true;
};
// all ok
static_assert([](auto x){ return decltype(x)::template v<>; }(s{}));
// clang ok, gcc ok, msvc nope
static_assert([](auto x){ return x.template v<>; }(s{}));
// clang nope, gcc nope, msvc ok
static_assert([](auto x){ return x.v<>; }(s{}));

实时示例


Clang的错误信息:
<source>:10:36: error: use 'template' keyword to treat 'v'
as a dependent template name
   10 | static_assert([](auto x){ return x.v<>; }(s{}));
      |                                    ^
      |                                    template 

MSVC的错误信息:
<source>(8): error C2187: syntax error: 'template' was unexpected here
<source>(8): note: see reference to function template instantiation
'auto <lambda_2>::operator ()<s>(_T1) const' being compiled
        with
        [
            _T1=s
        ]

(auto x)替换为(s x),然后它们都能正常工作。 所以编译器在推断auto时遇到了问题。 - undefined
5
我会说MSVC错了。 - undefined
3
在这个上下文中,auto 使得 lambda 函数成为模板化的。即使它们被调用时使用了 s,它们的函数体必须对任何类型都有效;编译器不能假设 x 是一个 s(除非是对于非可观察的情况,根据 as-if 规则)。 - undefined
尽管不是规范的,但这个示例表明.template v<>是正确的形式:p->X<0>::f(); // error: A​::​X not found in ((p->X) < 0) > ​::​f()。 我正在尝试理解为什么[basic.lookup.qual.general]/3是答案。 - undefined
1个回答

5
简而言之,xdecltype(x)是相关的,而template在所有情况下都是允许和必要的。这是一个MSVC编译器的错误。
关于相关类型,包括decltype,C++标准的措辞如下:
一个类型是相关的,如果它是:
- 模板参数, - [...] - 由decltype(expression)表示,其中expression是类型相关的。
- [temp.dep.type] p7.10 此外,x是类型相关的,因为(参见[temp.dep.expr] p3.1)它与一个或多个声明通过名称查找关联,这些声明声明了一个相关类型。 auto参数用于泛型lambda,使调用运算符成为一个函数模板,因此,x与一个模板参数相关联,这是一个依赖关系。 因此,decltype(x)::template v<>中需要使用template关键字。所有编译器都认同这一点,这是正确的。
在这个语句中,
return x.template v<>;

x是类型相关的,因为它通过名称查找与通用lambda的调用运算符的隐式模板参数相关联。因此,template是必需的。


注意:除了标准用语之外,直观上是必要的,因为如果不知道v是一个模板,x.v <可以被解释为"x.v小于..."。这在[temp.names] p3中正式说明。

另请参阅:在哪里以及为什么我必须放置"template"和"typename"关键字?

MSVC编译器错误

MSVC不允许.template v<>显然是一个错误。值得注意的是,MSVC接受以下写法:

[](s x){ return x.template v<>; }(s{})

然而,将参数类型从s改为auto会导致编译错误。这完全没有任何意义,因为templatetypename是可选的,不必要地添加它们永远不应该导致失败。
注意:我在Microsoft Developer Community中找不到现有的错误报告,所以可能还没有人报告过这个问题。你可能想要报告这个错误。

我已经尽可能多地阅读了关于依赖查找的资料,但是找不到支持“因此,template是必需的”这一观点的引用。甚至找不到有关template_name < template_parameter_list > ...( template_name < template_parameter_list ) > ...存在歧义的引用。 - undefined
@YSC,这个答案中有,尽管是在注释中。请参阅https://eel.is/c++draft/temp.names#3。`template`是必需的,因为没有满足任何一个条件,所以`<被解释为小于运算符。第一个条件说明template将解析<`作为模板参数列表的分隔符,这正是我们所需要的。如果它被解析为小于运算符,程序将是非法的。 - undefined
还要注意,第二个要点不适用,因为名称查找无法找到相关的依赖成员。尽管如此,这些都超出了本回答的范围,而且实际上应该放在在哪里以及为什么我必须使用"template"和"typename"关键字?中讨论。 - undefined
我完全错过了这个项目/3,有点吓人。非常感谢。 - undefined
看起来这个bug相当明显,我不确定它是如何在这些年里被忽略的。在你的调查工作的帮助下,我已经向微软报告了这个bug。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接