为什么"A <0> = 0"中的template-id由于大于或等于运算符"> ="而无法在没有空格的情况下进行编译?

Col*_*mbo 27 c++ templates language-lawyer c++11

template <int>
using A = int;

void f(A<0>=0);   // Attempting to declare a function f taking int,
                  // with the default argument 0

// Works as expected:
// void f(A<0> = 0);
Run Code Online (Sandbox Code Playgroud)

这既不适用于GCC 4.9.2也不适用于Clang 3.5 - 更不用说ICC或VC++了.显然,该>=位被解析为大于或等于运算符.但是,对于[temp.names]/3,这似乎是不正确的:

名称查找(3.4)后发现名称是模板名称或者operator-function-idliteral-operator-id引用一组重载函数,其中任何成员都是函数模板,如果遵循此函数通过a <,<始终将其视为template-argument-list的分隔符,而不是less-than运算符. 在解析模板参数列表时,第一个非嵌套 >138被视为结束分隔符而不是大于运算符.[..] [ 例如:

template<int i> class X { /* ...*/ };

X< 1>2 > x1; // syntax error
X<(1>2)> x2; // OK
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

138)一种>包围型-IDdynamic_cast, static_cast,reinterpret_castconst_cast,或包围 模板参数个后续的模板id,被认为是嵌套出于本说明书的目的.

我错过了什么或这是一个编译器错误?

Sha*_*our 18

这是最大munch原则的效果,它具有词法分析器,尽可能多地使用字符来形成有效的令牌.草案C++标准部分2.5 [lex.pptoken]中对此进行了介绍,其中说:

否则,下一个预处理标记是可以构成预处理标记的最长字符序列,即使这会导致进一步的词法分析失败.

如上所述的任何情况都需要特定的例外情况,例如这种情况<::,我们可以在下面的代码中看到一个例子:

template<typename T> class SomeClass;
class Class;

SomeClass<::Class>* cls;
Run Code Online (Sandbox Code Playgroud)

这个问题涉及的例外情况,例外情况在最大蒙克规则正上方的子弹中列出:

否则,如果接下来的三个字符是<::且后续字符既不是:nor>,则<被视为预处理器标记本身而不是备用标记<:的第一个字符.

当然还有>你在问题中引用的非嵌套.

注意我们可以看到这>=是来自2.13 [lex.operators]部分的预处理器令牌,它说:

C++程序的词汇表示包括许多预处理标记,这些标记在预处理器的语法中使用,或者转换为运算符和标点符号的标记:

并包含>=在列表中.

>>修复

我们可以从提案中看到修复>>案例:N1757:直角支架(强调我的):

自从引入尖括号以来,C++程序员一直惊讶于两个连续的直角括号必须用空格分隔:

#include <vector>
typedef std::vector<std::vector<int> > Table;  // OK
typedef std::vector<std::vector<bool>> Flags;  // Error
Run Code Online (Sandbox Code Playgroud)

问题是"最大咀嚼"原则直接后果,以及>>是C++中的有效令牌(右移)这一事实.

这个问题是一个小问题,但是持续存在,令人讨厌,有些令人尴尬.如果成本合理,那么消除意外似乎是值得的.

本文档的目的是解释允许>>被视为两个闭合尖括号的方法,以及讨论产生的问题.提出了一个具体的选择以及将在当前工作文件中实施该提案的措辞.

同时指出>=案例:

值得注意的是,>> =和> =令牌也会出现问题.例如

void func(List<B>= default_val1);
void func(List<List<B>>= default_val2);
Run Code Online (Sandbox Code Playgroud)

这两种形式目前都是不正确的.可能还希望解决这个问题,但本文并未提议这样做.

注意,这个改变打破了与C++ 03的向后兼容性.