【问题标题】:How does a parser for C++ differentiate between comparisons and template instantiations?C++ 的解析器如何区分比较和模板实例化?
【发布时间】:2016-05-25 07:09:20
【问题描述】:

在 C++ 中,符号“”用于比较以及表示模板参数。因此,代码 sn -p

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

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

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

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

【问题讨论】:

  • 您可以通过检查什么是 LL 语法来很好地了解会发生什么。 ;-)
  • @Caduchon AFAIK C++ 语法既不是 LL 也不是 LR。事实上,我相信它根本不是上下文无关的。答案中暗示了这一点,其中有关上下文的知识似乎是正确解析所必需的。
  • @Bakuriu:大多数传统语法(左侧单非终结符)根据定义都是上下文无关的。 C++ 不会改变这一点。对于大多数语言,语法的解释在实践中不是上下文无关的。 C++ 在这方面没有什么不同。
  • @Askaga:您将狭义的“解析”与确定语法的含义混淆,其中一部分是解析源代码中每个点使用的每个标识符的含义(“名称分辨率”。

标签: c++ parsing syntax compiler-construction


【解决方案1】:

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

如果Foo 不是模板名称,则Foo &lt; Bar 是一个比较。

在大多数情况下,Foo 是什么是已知的,因为标识符通常必须在使用前声明,因此选择一种方式或其他方式没有问题。但是有一个例外:解析模板代码。如果Foo&lt;Bar&gt;在模板内部,Foo的含义依赖于模板参数,则不知道Foo是否是模板。语言标准指示将其视为非模板,除非前面有关键字 template。

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

【讨论】:

  • 添加到这个答案:当编译器没有上下文时,例如当Foo 是一个依赖名时,你需要告诉编译器它是一个模板:template Foo&lt;Bar&gt;
  • 我明白了。因此,由于在 C++ 中,所有内容都必须在使用之前进行声明,因此词法分析器和解析器阶段可以相互关联,并且解析器不断将当前类型和变量名称反馈给词法分析器,因此它可以生成最可能的标记,或者 标识符或类型标识符,对吗?如果无法确定,程序员必须在类型标识符前面加上“模板”关键字。
  • 实际上,词法分析器通常区分至少三种标识符:“模板”、“类型定义/类名”和“普通标识符”。对于未知的情况有两个不同的前缀:template 和 typename。
  • Foo&lt;Bar&gt; 是一个 template-id。
【解决方案2】:

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

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

关键字template 帮助编译器了解正在发生的事情,但在大多数情况下,编译器只是知道,因为&lt; 在另一方面没有意义 - 如果它在任何一个方面都没有意义形式,那么它就是一个错误,那么这只是试图弄清楚程序员可能想要什么的问题 - 这就是有时,一个简单的错误(例如缺少}或template)可以导致整个解析误入歧途并导致成百上千的错误[尽管理智的编译器会在合理的数量后停止,以免错误消息填满整个宇宙]

【讨论】:

    【解决方案3】:

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

    有一些困难的情况,当你真的必须指导解析器时。例如,假设正在编写一个带有模板成员函数的类模板,您想明确地对其进行专门化。您可能必须使用如下语法:

     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

    【讨论】:

    • 好点。这是一个离题的问题。假设 C++ 设计者或新语言的设计者决定为模板参数使用另一个符号而不是 &lt; &gt;。候选人( )、[ ] 和{ } 用于其他目的。有没有其他替代语言?
    【解决方案4】:

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

    你可以do these tasks separately.。

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2019-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 2015-05-05
      • 1970-01-01
      相关资源
      最近更新 更多