【问题标题】:Is a throw exception evaluated by the abstract machine?抽象机是否评估了抛出异常?
【发布时间】:2021-01-10 07:52:03
【问题描述】:

根据intro.abstract#1

... 需要符合要求的实现来模拟(仅)抽象机的可观察行为,如下所述。

这个解释是as-ifrule,其中包含如下例子:

...如果一个实际的实现可以推断出它的值没有被使用并且没有产生影响程序可观察行为的副作用,则它不需要评估表达式的一部分。

副作用的定义在intro.execution#7:

读取由 volatile glvalue ([basic.lval]) 指定的对象、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用,它们是执行环境的状态。 ...

在我看来,在以下程序中:

int main() 
{
  throw 42;
}

没有使用表达式throw 42; 的值,它不满足任何作为副作用的标准。

这是否意味着允许实现不对这个表达式求值?上面的程序是否相当于:

int main() {}

就抽象机而言?我找不到任何说明抽象机器知道或关心异常的文本。

【问题讨论】:

标签: c++ exception language-lawyer semantics throw


【解决方案1】:

再一次,我被证明是正确的“as if rule”:在 C 或 C++ 中,没有哪个子句被更广泛地误解了。它甚至不是一个“规则”。它提醒编程语言标准描述的本质。

您只能将所谓的“规则”应用于程序;语句或表达式没有 as if 规则。 由于您没有格式良好且可以运行的程序,因此您的问题中没有所谓的适用规则

如果你有一个可以运行的程序,“规则”只是说一致性是通过测量程序 I/O 来定义的(包括 volatile 访问,它只是一种 I/O像 stdio,并从 main/calling exit 返回)。

总结:你的问题本身没有意义,你应该忘记有一种叫做“好像规则”的东西,因为它不能成为一个独特的、可移动的规则。

(这个答案适用于 C、C++、Java 以及几乎所有“高级”的东西。)

注意

表达式throw 42;的值没有被使用,也没有 满足任何作为副作用的标准。

因为 throw 表达式会抛出异常,所以它不会产生像 42 部分那样的“值”。它的作用是:抛出异常,停止正常的控制流并跳转到异常处理程序,如果没有异常处理程序,则完全退出程序。

就“抽象机器”而言,抛出肯定会做一些事情:它会停止抽象机器中的通常执行。在抽象机中,可以看到投掷​​的效果。

询问“抽象机器”似乎与您的全部观点相矛盾,因为任何计算在抽象机器中都是可见的,但抽象机器不可见

无论如何,所有这些论文只是证明了我的观点,即“抽象机器”、“好像规则”......是对任何人都没有帮助的有害概念

只考虑程序语义,在所有高级语言中,它总是以 I/O 的形式定义的。 忘记其余的!

【讨论】:

  • 感谢您的回答。关于 as-if 规则的 cmets 很有帮助。它并没有真正解决我的问题,所以我将表达式包装到一个程序中。很抱歉在您回答后(稍微)改变了问题。
  • 我的回答如何不适用于您的原始问题?什么不清楚或不完整?
  • 好的,我不认为你的答案不清楚。但是您最初的答案归结为““抽象机器”,“好像规则”......是对任何人都没有帮助的有害概念。”,您在编辑中明确表示。除了我不同意这一事实之外,我认为这个答案对于语言律师的问题没有用处,该问题询问这些术语的精确定义和含义,以及它们如何应用于特定程序。
  • @cigien 这些术语没有“精确定义”,因为它们在语言语义中没有任何用途。除了作为实际上使人们(像您一样)感到困惑的解释装置之外,它们什么也没做。抽象机是描述计算效果的方式。整个标准描述了抽象机。但它是抽象的,在实际程序上使用ptrace 可能并不总是显示与抽象机器中相同的计算。 它只是说通过调试器进行跟踪可能无法按预期工作。我不同意的事实”如果这样做,您能解释一下它们的用处吗?跨度>
【解决方案2】:

(已编辑)问题归结为是否允许编译器将int main() { throw 42; }“优化”为仅int main() { }。我相信在这种情况下答案是否定的,因为抛出的异常会导致程序的可观察行为发生变化,编译器需要模拟这种行为。

由于没有定义明确的处理程序,throw 42; 应该调用std::terminate(),而std::terminate() 最终调用std::abort(),其中“实现定义的状态返回到主机环境,表明执行失败”。换句话说,程序的退出代码与 vs. 不同,不会抛出异常,因此不能省略 throw

(这排除了没有“host”可返回的独立实现的情况,但我认为不允许编译器对其将运行的环境做出假设.)

在更一般的情况下,当用户代码中定义了显式匹配的异常处理程序时,它会变得更加模糊。抛出异常会将控制权转移到该处理程序,但如果编译器可以确定该处理程序不会导致可观察行为发生变化其他副作用,那么整个@987654331 @ 可能被优化了。这可能发生在处理程序不访问任何易失性数据、不修改任何对象、不调用任何库 I/O 函数并执行与正常程序等效的程序退出的情况下。

【讨论】:

  • "这忽略了独立实现的情况......但我认为不允许编译器对其将运行的环境做出假设。" 如果一个实现不能合法地评估代码,这在我看来就像抽象机器没有说明会发生什么。
  • @cigien 在the standardthis comment 之间听起来如果入口点是main 那么它必须返回一个int 并且编译器不能更改返回值。然而,一个独立的实现可以定义一个完全不同的入口点,例如void alt_main(); 根本没有返回值,因此没有什么可观察的。也就是说,对于真正的语言律师来说,这是一个很好的权衡点。
  • 是的,这很好。我怀疑这甚至可能没有指定:p 感谢您做出明确的回答,这表明了立场。 +1
  • "因此投掷不能被忽略" 所以它不能被“忽略”(替换为 NOP)但它可以替换为 abort跨度>
  • @curiousguy 在我的阅读中,问题关于“表达式throw 42;”没有定义处理程序,即未捕获的异常。而可观察的行为只是另一种说法。
猜你喜欢
  • 1970-01-01
  • 2014-04-23
  • 2012-08-22
  • 1970-01-01
  • 2017-08-06
  • 2022-07-29
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
相关资源
最近更新 更多