C ++解析器如何区分比较和模板实例化?

时间:2016-02-14 11:41:48

标签: c++ parsing syntax compiler-construction

在C ++中,符号'<'和'>'用于比较以及表示模板参数。因此,代码片段

[...] Foo < Bar > [...]

可能被解释为以下两种方式中的任何一种:

  • 具有模板参数Bar
  • 的Foo类型的对象
  • 将Foo与Bar进行比较,然后将结果与下一步的结果进行比较

C ++编译器的解析器如何有效地决定这两种可能性?

4 个答案:

答案 0 :(得分:14)

如果已知Foo是模板名称(例如template <...> Foo ...声明在范围内,或者编译器看到template Foo序列),则Foo < Bar不能一个对比。它必须是模板实例化的开始(或本周调用的Foo < Bar >)。

如果Foo 不是模板名称,那么Foo < Bar就是比较。

在大多数情况下,知道Foo是什么,因为标识符通常必须在使用前声明,因此决定一种方式或另一种方式没有问题。但是有一个例外:解析模板代码。如果Foo<Bar>位于模板中,并且Foo的含义取决于模板参数,则不知道Foo是否为模板。语言标准会将其视为非模板,除非前面有关键字template

解析器可以通过将上下文反馈给词法分析器来实现此目的。词法分析器将Foo识别为不同类型的标记,具体取决于解析器提供的上下文。

答案 1 :(得分:10)

要记住的重要一点是C ++语法不是无上下文的。即,当解析器看到Foo < Bar(在大多数情况下)知道Foo引用模板定义时(通过在符号表中查找),因此<不能进行比较

当你真的需要引导解析器时,有一些困难的情况。例如,假设正在编写具有模板成员函数的类模板,您希望将其显式专门化。您可能必须使用如下语法:

 a->template foo<int>();

(在某些情况下;有关详细信息,请参阅Calling template function within template class

此外,非类型模板参数内的比较必须用括号括起来,即:

foo<(A > B)>

foo<A > B>

非静态数据成员初始化程序带来更多乐趣:http://open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#325

答案 2 :(得分:5)

C和C ++解析器是“上下文敏感的”,换句话说,对于给定的令牌或词汇,它不能保证是不同的并且只有一个含义 - 它取决于使用令牌的上下文。 / p>

因此,编译器的解析器部分将知道(通过理解“它在源中的位置”)它正在解析某种类型或某种比较(这不是很容易知道,这就是为什么阅读有能力的C或C ++编译器的来源并不完全是直接的 - 有很多条件和函数调用检查“这是其中之一,如果这样做,否则做其他事情”)。

关键字template有助于编译器理解发生了什么,但在大多数情况下,编译器只是知道因为<在另一方面没有意义 - 如果不是在EITHER表单中有意义,那么这是一个错误,所以这只是试图弄清楚程序员可能想要什么的问题 - 这是有时候的一个原因,一个简单的错误,例如遗漏{{1} }或}可能导致整个解析误入歧途并导致数百或数千个错误[虽然理智的编译器在合理的数字后停止,但没有用错误消息填充整个Universe]

答案 3 :(得分:2)

这里的大多数答案都会混淆确定符号的含义(我称之为#34;名称解析&#34;)与解析(狭义地定义为&#34;可以读取语法该计划&#34;)。

你可以do these tasks separately.

这意味着您可以为C ++构建一个完全无上下文的解析器(正如我的公司,Semantic Designs所做的那样),并留下决定符号含义的明确单独后续任务的问题。< / p>

现在,该任务由源代码的可能语法解释驱动。在我们的解析器中,这些在解析中被捕获为歧义

名称解析的作用是收集有关名称声明的信息,并使用该信息来确定哪些不明确的解析没有意义,并简单地删除它们。剩下的是一个有效的解析,只有一个有效的解释。

在实践中完成名称解析的机制是一个很大的混乱。但那是C ++委员会的错,而不是解析器或名称解析器。使用我们的工具去除歧义实际上是自动完成的,这使得该部分实际上非常好,但是如果你不查看我们的工具你就不会欣赏它,但我们这样做是因为它意味着一个小型工程团队能够构建它

See an example of resolution of template-vs-less than on C++s most vexing parse由我们的解析器完成。