【问题标题】:Are tautologies in compile-time evaluated code guaranteed to be executed / optimized away?编译时评估代码中的重言式是否保证被执行/优化?
【发布时间】:2020-03-24 12:51:56
【问题描述】:

编译器是否保证在 constexpr 环境中计算 "tautologies" 的布尔 constexpr 表达式(例如始终分别为 truefalse)?

最小示例/说明

例如,在下面的代码片段中(在标有(1) 的行处)我在constexpr 环境中调用了一个函数,我打算在传递non-constexpr 函数时导致编译时错误。至少我使用的编译器 (g++-10.0) 可以做到这一点,即使它也可以意识到表达式始终为 true 而无需对其进行评估。我问这个问题的原因是因为——据我所知——在非 constepxr 上下文中,像 i >= std::numeric_limits<int>::min() 这样的表达式被优化为 trueint i

#include <limits>
constexpr int example_function() { return 1;}
constexpr bool compileTimeErrorDesired = example_function() || true; // (1)

应用示例

如果(1) 中的行为得到保证,它可以例如在concept 中使用,以执行不同的代码,具体取决于作为模板参数提供的函数是否可以在编译时进行评估。我实现了一个非常短的 (7 lines-of-code) 示例,它完全符合 here at compiler explorer

问题

如果使用非 constexpr 函数调用第 (1) 行,是否保证会导致编译时错误?

编辑插入说明,根据反馈简化示例。

【问题讨论】:

  • 你的意思是example_function不是constexpr?如果example_function 不是constexpr,则example_function() || true 不是常量表达式,编译器需要诊断出static_assert 的非常量表达式。
  • 您所说的“具有更明显的重言式,不会引发编译时错误”是什么意思?如果example_function 没有声明为constexpr,那么g++ 会正确拒绝static_assert(example_function() || true)
  • 保证什么行为?我完全不明白这个问题在问什么。
  • 保证。根据 C++ 规则,example_function() || true 不是常量表达式,即使它的值在数学上是常量。
  • IMO 问题实际上是指“AS IF 规则”,这很好explained hereorder of evaluating operator arguments 也对这个问题有影响。

标签: c++ language-lawyer constexpr c++20 c++-concepts


【解决方案1】:

如果f 不是 constexpr 函数,则保证f() || true 不是核心常量表达式:(常量表达式比核心常量表达式更严格)[expr.const]/2.2

表达式e 是一个核心常量表达式,除非 评估e遵循抽象机的规则, 将评估以下表达式之一:

  • [...]

  • 对文字类的 constexpr 构造函数以外的函数的调用、constexpr 函数或对 普通析构函数 ([class.dtor]) [注意: 重载解析 照常应用 —尾注];

  • [...]

如果在需要常量表达式的上下文中使用非常量表达式,则还可以保证程序格式错误(需要诊断)。请注意,|| 定义为从左到右计算:[expr.log.or]/1

|| 运算符从左到右分组。操作数都是 上下文转换为bool。如果其中任何一个,它会返回 true 操作数为true,否则为false。与| 不同,|| 保证 从左到右的评估;此外,第二个操作数不是 如果第一个操作数的计算结果为真,则计算。

换句话说,true || f() 是一个核心常量表达式,因为f() 没有被计算,而f() || true 不是因为f() 被计算了。

表达式是否为常量表达式与优化无关——常量表达式是根据抽象机的规则定义的。

【讨论】:

  • 虽然如果它必须是 CTCE,因为编译器必须评估它以证明它已经具有价值。我不知道有什么会因为扔掉它而感到悲观,尽管好像允许这样。
  • @Deduplicator 请问……CTCE代表什么?
  • 编译时常量表达式。
【解决方案2】:

当涉及到常量表达式时,术语“执行”并不完全合适。甚至“已评估”也可能要谨慎使用,因为表达式是否为常量表达式部分取决于表达式被评估时会发生的行为,但不被视为最严格意义上的评估。

[expr.const] 描述了对“编译时行为”的许多不同上下文的要求,包括“常量表达式”。 [expr.const]/(5.2) 表示如果计算一个表达式将计算一个非 constexpr 函数,则该表达式不是核心常量表达式,因此不是一个常量表达式。如果在需要常量表达式的上下文中使用表达式(如static_assert、非类型模板参数等),则程序格式错误,必须有诊断消息。如果表达式是转换后的常量表达式并且即使不是常量表达式也可以确定表达式的结果值,则没有规则允许在这样的上下文中允许非常量表达式或跳过假设评估的某些部分。

因此,如果example_function 未声明为constexpr,则example_function() || true 不是常量表达式,因为评估需要调用函数。但是true || example_function() 是一个常量表达式,因为计算不会调用函数。

您的is_constexpr&lt;T&gt; 保证可以工作,因为任何涉及 requires-expression 内模板参数的语义约束违规都不会使程序格式错误,而只会使 requires -表达式 结果值false ([expr.prim.req]/6)。在is_constexpr&lt;example_function&gt; 中,使用非常量表达式T() 作为std::enable_if 的模板参数,通过ConstexprHelper&lt;T&gt; 实例化的默认模板参数是这样的语义错误,所以 requires-expression有值false

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-05
    • 1970-01-01
    • 2015-09-16
    • 2015-09-12
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 2022-11-18
    相关资源
    最近更新 更多