繁体   English   中英

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

[英]How does a parser for C++ differentiate between comparisons and template instantiations?

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

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

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

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

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

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

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

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

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

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

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

 a->template foo<int>();

(在某些情况下;有关详细信息,请参阅模板类中的调用模板函数

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

foo<(A > B)>

foo<A > B>

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

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

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

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

这里的大多数答案混淆了确定符号的含义(我称之为“名称解析”)和解析 (狭义地定义为“可以读取程序的语法”)。

您可以单独执行这些任务。

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

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

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

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

查看一个解决模板vs-less的解决方案的例子,而不是C ++我们的解析器完成最令人烦恼的解析

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM