编译时评估代码中的重言式是否保证被执行/优化掉?
Are tautologies in compile-time evaluated code guaranteed to be executed / optimized away?
编译器是否保证在 constexpr
环境中评估 "tautologies"
的布尔 constexpr
表达式(例如始终分别为 true
或 false
)?
最小示例/说明
例如,在下面的代码片段中(在标有 (1)
的行)我在 constexpr
环境中调用了一个函数,我打算在 constexpr
环境中引发编译时错误 non-constexpr
函数通过。至少我使用的编译器 (g++-10.0
) 会这样做,即使它也可以意识到表达式总是 true
而无需对其进行评估。我问这个问题的原因是——据我所知——在非 constepxr 上下文中,像 i >= std::numeric_limits<int>::min()
这样的表达式被优化为 true
以获得 int 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) 行是否保证会导致编译时错误?
编辑 插入说明,根据反馈简化示例。
如果f
不是constexpr函数,则保证f() || true
不是核心常量表达式:(常量表达式比核心常量表达式更严格)[expr.const]/2.2
An expression e
is a core constant expression unless the
evaluation of e
, following the rules of the abstract machine,
would evaluate one of the following expressions:
[...]
an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a
trivial destructor ([class.dtor]) [ Note: Overload resolution
is applied as usual — end note ];
[...]
如果在需要常量表达式的上下文中使用非常量表达式,也可以保证程序格式错误(需要诊断)。请注意,||
定义为从左到右计算:[expr.log.or]/1
The ||
operator groups left-to-right. The operands are both
contextually converted to bool
. It returns true
if either of its
operands is true
, and false
otherwise. Unlike |
, ||
guarantees
left-to-right evaluation; moreover, the second operand is not
evaluated if the first operand evaluates to true.
换句话说,true || f()
是一个核心常量表达式,因为f()
没有计算,而f() || true
不是因为f()
被计算。
表达式是否为常量表达式与优化无关——常量表达式是根据抽象机的规则定义的。
术语 "executed" 在涉及常量表达式时并不完全合适。即使 "evaluated" 也可能要小心使用,因为表达式是否是常量表达式部分取决于表达式被求值时会发生什么的行为,但在最严格的意义上不被视为求值。
[expr.const] 描述了 "compile-time behavior" 的许多不同上下文的要求,包括 "constant expression"。 [expr.const]/(5.2) 表示,如果计算一个表达式将计算一个非 constexpr 函数,则该表达式不是核心常量表达式,因此不是常量表达式。如果在需要常量表达式(如 static_assert
、非类型模板参数等)的上下文中使用表达式,则程序格式错误,并且必须有诊断消息。没有规则允许在这样的上下文中允许非常量表达式,或者如果表达式是转换后的常量表达式并且尽管不是常量表达式也可以确定表达式的结果值,则跳过假设评估的某些部分。
因此,如果未声明 [=11=] constexpr
,则 example_function() || true
不是常量表达式,因为计算需要调用函数。但是 true || example_function()
是一个常量表达式,因为计算不会调用该函数。
你的 is_constexpr<T>
保证可以工作,因为在 requires-expression 中涉及模板参数的任何语义约束违规不会使程序格式错误,但是只是使 requires-expression 结果值 false
([expr.prim.req]/6)。在 is_constexpr<example_function>
中,通过 ConstexprHelper<T>
实例化的默认模板参数使用非常量表达式 T()
作为 std::enable_if
的模板参数是这样一个语义错误,因此 requires-expression 具有值 false
.
编译器是否保证在 constexpr
环境中评估 "tautologies"
的布尔 constexpr
表达式(例如始终分别为 true
或 false
)?
最小示例/说明
例如,在下面的代码片段中(在标有 (1)
的行)我在 constexpr
环境中调用了一个函数,我打算在 constexpr
环境中引发编译时错误 non-constexpr
函数通过。至少我使用的编译器 (g++-10.0
) 会这样做,即使它也可以意识到表达式总是 true
而无需对其进行评估。我问这个问题的原因是——据我所知——在非 constepxr 上下文中,像 i >= std::numeric_limits<int>::min()
这样的表达式被优化为 true
以获得 int 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) 行是否保证会导致编译时错误?
编辑 插入说明,根据反馈简化示例。
如果f
不是constexpr函数,则保证f() || true
不是核心常量表达式:(常量表达式比核心常量表达式更严格)[expr.const]/2.2
An expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor ([class.dtor]) [ Note: Overload resolution is applied as usual — end note ];
[...]
如果在需要常量表达式的上下文中使用非常量表达式,也可以保证程序格式错误(需要诊断)。请注意,||
定义为从左到右计算:[expr.log.or]/1
The
||
operator groups left-to-right. The operands are both contextually converted tobool
. It returnstrue
if either of its operands istrue
, andfalse
otherwise. Unlike|
,||
guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.
换句话说,true || f()
是一个核心常量表达式,因为f()
没有计算,而f() || true
不是因为f()
被计算。
表达式是否为常量表达式与优化无关——常量表达式是根据抽象机的规则定义的。
术语 "executed" 在涉及常量表达式时并不完全合适。即使 "evaluated" 也可能要小心使用,因为表达式是否是常量表达式部分取决于表达式被求值时会发生什么的行为,但在最严格的意义上不被视为求值。
[expr.const] 描述了 "compile-time behavior" 的许多不同上下文的要求,包括 "constant expression"。 [expr.const]/(5.2) 表示,如果计算一个表达式将计算一个非 constexpr 函数,则该表达式不是核心常量表达式,因此不是常量表达式。如果在需要常量表达式(如 static_assert
、非类型模板参数等)的上下文中使用表达式,则程序格式错误,并且必须有诊断消息。没有规则允许在这样的上下文中允许非常量表达式,或者如果表达式是转换后的常量表达式并且尽管不是常量表达式也可以确定表达式的结果值,则跳过假设评估的某些部分。
因此,如果未声明 [=11=] constexpr
,则 example_function() || true
不是常量表达式,因为计算需要调用函数。但是 true || example_function()
是一个常量表达式,因为计算不会调用该函数。
你的 is_constexpr<T>
保证可以工作,因为在 requires-expression 中涉及模板参数的任何语义约束违规不会使程序格式错误,但是只是使 requires-expression 结果值 false
([expr.prim.req]/6)。在 is_constexpr<example_function>
中,通过 ConstexprHelper<T>
实例化的默认模板参数使用非常量表达式 T()
作为 std::enable_if
的模板参数是这样一个语义错误,因此 requires-expression 具有值 false
.