【问题标题】:In the C++ standard does well-formed means that the code compiles?在 C++ 标准中,格式正确是否意味着代码可以编译?
【发布时间】:2020-06-16 13:30:27
【问题描述】:

C++ 标准将well-formed programs 定义为

根据语法规则构造的C++程序,可诊断 语义规则和单定义规则

我想知道是否所有格式良好的程序都可以编译(如果不是这种情况,什么类型的错误会影响格式正确的程序和可编译问题之间的差异)。例如,包含歧义错误的程序是否会被视为格式正确?

【问题讨论】:

  • 歧义违反了可诊断的语义规则,所以不,这不是很好的格式。
  • 所有格式正确的程序都会编译(假设编译器正确)。但不是相反。例如,违反“单一定义规则”(ODR)的行为经常(甚至大部分)不会被编译系统发现。
  • @davidbak 程序可能会溢出编译器限制...
  • @curiousguy - C++ 标准没有说明允许编译器在哪里有限制,甚至可能是这些限制的下限? (我不知道,这就是我问的原因。)
  • @davidbak 确切地说,编译器可以有限制(文件中的行、函数中的语句、模板实例的嵌套......)。对于这些限制,标准中有推荐的最小值(据我所知)。这是实现不执行其工作的合法通配符。但是,如果有一个“符合”但限制很小的实现是不诚实的。

标签: c++ language-lawyer standards well-formed


【解决方案1】:

格式良好的程序可以有未定义的行为。

它在注释中,因此在技术上不具有权威性,但似乎有意终止编译(或标准所称的“翻译”)在可能的 UB 范围内:

[intro.defs]

未定义的行为

本文档没有要求的行为
[注意:当本文档省略任何明确的行为定义或程序使用错误的构造或错误数据时,可能会出现未定义的行为。

允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(无论是否发出诊断消息),到 终止翻译 或执行(发出诊断消息)。

许多错误的程序结构不会产生未定义的行为;他们需要被诊断出来。

常量表达式的求值永远不会表现出在本文档的 [intro] 到 [cpp] ([expr.const]) 中明确指定为未定义的行为。 ——尾注 ]


还有实际的实施限制:

[隐含]

由于计算机是有限的,C++ 实现不可避免地受限于它们可以成功处理的程序的大小。
每个实施都应在已知的情况下记录这些限制。本文档可能会引用存在的固定限制,说明如何根据可用资源计算可变限制,或者说固定限制不存在或未知。


此外,编译器可能存在并且确实存在错误。格式良好只是意味着符合标准的编译器应该编译它(在上述限制范围内)。有缺陷的编译器不一定符合标准。


最后,标准文档本身是not perfect。如果对规则的含义存在分歧,那么程序有可能在一种解释下是良构的,而在另一种解释下是良构的。

如果编译器不同意程序员或其他编译器,那么它可能无法编译对方认为格式正确的程序。

【讨论】:

    【解决方案2】:

    我想知道是否所有格式正确的程序都可以编译

    当然不是,实际上。

    一个典型的例子是当你在一个巨大的 translation unit 上请求optimizations 包含长的C++ 函数。

    (但理论上是的)

    当然参见n3337 C++11 标准或C++17 标准。

    这发生在我的(旧)GCC MELT 项目中。我正在生成GCC 编译的C++ 代码,基本上是在我发明的Lispy DSL 上使用转译器(或source to source compilation)技术来生成GCC plugins 的C++ 代码。另见thisthat

    在实践中,如果你生成一个单个十万条语句的C++函数,编译器在优化它时会遇到麻烦。

    在 GUI 代码生成器(例如 FLUID)或某些解析器生成器(例如 ANTLR(当底层输入语法设计不佳时)、接口生成器例如 SWIG 或通过使用诸如GPPGNU m4 之类的预处理器(就像GNU autoconf 一样)。 C++ template 扩展也可能产生任意大的函数(例如,当您将多个 C++ container 模板和 ask GCC 编译器在链接时与 g++ -flto -O2 组合到 optimize 时)

    我做过基准测试,并在过去十年中通过实验观察到,编译 n 个语句的 C++ 函数可能需要 O(n2) time(和 IIRC O(n log n) space)和 g++ -O3。请注意,一个好的优化 C++ 编译器必须做到register allocationloop unrollinginline expansion,一些ABIs(包括Linux/x86-64)要求传递或返回small struct -s(或小class-s 的实例)通过寄存器。所有这些优化都需要权衡取舍,并且正在触及combinatorial explosion 的一些障碍:实际上,编译器优化至少是intractable problem,并且可能是undecidable。另请参阅相关的Rice's theorem 并阅读Dragon Book

    您可以调整我的 manydl.c 程序(生成或多或少的随机 C 代码,编译为多个 plugins,然后在 Linux 上编译为 dlopen-ing 它们)以发出 C++。然后,您将能够进行一些 GCC 编译器基准测试,因为 manydl 程序能够生成数十万个插件,其中包含许多或多或少随机的 C 函数。请参阅 Drepper 的论文 how to write shared libraries 并注意 libgccjit

    另请参阅 blog of the late Jacques Pitrat (1934-oct.2019) 以获取 C program 生成其自己的 C 代码的 50 万行的示例,其设计在 this paperthat book 中进行了说明。

    阅读Thriving in a crowded and changing world: C++ 2006--2020

    【讨论】:

    • 这不是我所期望的答案,但您指出它是绝对正确的!事实上,对于一些编译器和一些源代码,它可以在没有优化的情况下发生:对于某些特性,一些编译器具有效率低下的算法(例如,指数行为),这些算法不会被触发为“正常”或“合理”代码,而某人实际上会写,但你可能会运气不好发现生成的代码(有时很少重复构造......)(也很好的链接!)
    猜你喜欢
    • 2022-10-04
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 2017-05-10
    • 2011-04-26
    • 2011-01-28
    • 1970-01-01
    相关资源
    最近更新 更多