编译器是否保证评估在constexpr
环境中"tautologies"
(例如,分别始终true
或false
)的布尔constexpr
表达式?
最小示例/说明
例如,在下面的代码截图(在标有(1)
的行)中,我在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
)的示例,该示例在编译器资源管理器中正是这样做的。
问题
如果使用非 constexpr 函数调用第 (1) 行,是否保证会导致编译时错误?
编辑插入的说明,由于反馈而简化示例。
如果不是constexpr 函数,则保证f() || true
不是核心常量表达式f
:(常量表达式比核心常量表达式更严格)[expr.const]/2.2
表达式
e
是核心常量表达式,除非e
的评估,遵循抽象机器的规则, 将计算以下表达式之一:
- 对文本类的 constexpr
[...]
构造函数以外的函数的调用、constexpr 函数或隐式调用 普通析构函数 ([class.dtor]) [ 注意:重载解析 照常应用 — 尾 注 ];
[...]
如果在需要常量表达式的上下文中使用非常量表达式,还可以保证程序格式不正确(需要诊断)。 请注意,||
定义为从左到右计算:[expr.log.or]/1
||
运算符从左到右分组。操作数都是 上下文转换为bool
.它返回true
如果其 操作数是true
的,否则false
。与|
不同,||
保证 从左到右的评估;而且,第二个操作数不是 如果第一个操作数的计算结果为 true,则进行计算。
换句话说,true || f()
是一个核心常量表达式,因为没有计算f()
,而f() || true
不是因为f()
被计算。
表达式是否是常量表达式与优化无关 — 常量表达式是根据抽象机器的规则定义的。
术语"执行"在常量表达式方面并不完全合适。甚至"已评估"也可以谨慎使用,因为表达式是否为常量表达式部分取决于计算表达式时会发生什么行为,但不被视为最严格意义上的计算。
[expr.const] 描述了对"编译时行为"的多种不同上下文的要求,包括"常量表达式"。[expr.const]/(5.2) 表示,如果计算表达式将计算非 constexpr 函数,则该表达式不是核心常量表达式,因此不是常量表达式。如果在需要常量表达式(如static_assert
、非类型模板参数等)的上下文中使用表达式,则程序格式不正确,必须有诊断消息。没有规则允许在这样的上下文中允许非常量表达式或跳过假设计算的某些部分,如果表达式是转换后的常量表达式,并且尽管不是常量表达式,但表达式的结果值可以确定。
因此,如果example_function
没有声明为constexpr
,则example_function() || true
不是常量表达式,因为求值需要调用函数。但true || example_function()
是一个常量表达式,因为计算不会调用该函数。
您的is_constexpr<T>
可以保证正常工作,因为任何涉及requires-expression中的模板参数的语义约束违规都不会使程序格式错误,而只会使requires-expression结果值false
([expr.prim.req]/6)。在is_constexpr<example_function>
中,使用非常量表达式T()
作为模板参数来std::enable_if
通过ConstexprHelper<T>
实例化的默认模板参数是这样的语义错误,因此requires表达式具有false
值。