简体   繁体   中英

Are tautologies in compile-time evaluated code guaranteed to be executed / optimized away?

Is the compiler guaranteed to evaluate boolean constexpr expressions that are "tautologies" (eg always true or false respectively) in a constexpr environment?

Minimal Example / Clarification

For example, in the following code snipplet (at the line marked with (1) ) I invoke a function in a constexpr environment, which I intend to cause a compile time error whenever a non-constexpr function is passed. At least the compiler I use ( g++-10.0 ) does this, even though it could also realize that the expression is always true without evaluating it. The reason I ask this question, is because -- to the best of my knowledge -- in a non-constepxr context, an expression like i >= std::numeric_limits<int>::min() is optimized away to true for an int i .

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

Application Example

If the behavior in (1) is guaranteed, it can for instance be used in a concept , in order to execute different code, depending on whether a function provided as template argument can be evaluated at compile time. I implemented a very short ( 7 lines-of-code ) example which does exactly that here at compiler explorer .

Question

Is the line (1) guaranteed to cause a compile time error if called with a non-constexpr function?

edit Inserted clarification, simplify example due to feedback.

It's guaranteed that f() || true f() || true is not a core constant expression if f is not a constexpr function: (constant expressions are more restrictive than core constant expressions) [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 ];

  • [...]

It's also guaranteed that the program is ill-formed (diagnostic required) if non-constant expressions are used in contexts that require constant expressions. Note that || is defined to evaluate from left to right: [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.

In other words, true || f() true || f() is a core constant expression because f() is not evaluated, whereas f() || true f() || true is not because f() is evaluated.

Whether or not an expression is a constant expression has nothing to do with optimization — constant expressions are defined based on the rules of the abstract machine.

The terms "executed" isn't exactly appropriate when it comes to constant expressions. Even "evaluated" might be used with care, because whether an expression is a constant expression depends partly on behavior of what would happen if the expression were evaluated, but is not considered an evaluation in the most strict sense.

[expr.const] describes requirements for a number of different contexts of "compile-time behavior", including "constant expression". [expr.const]/(5.2) says that if evaluating an expression would evaluate a non-constexpr function, the expression is not a core constant expression, and therefore not a constant expression. If the expression is used in a context requiring a constant expression (like static_assert , a non-type template argument, etc.), the program is ill-formed and there must be a diagnostic message. There is no rule permitting anything like allowing a non-constant expression in such a context or skipping some parts of the hypothetical evaluation if the expression is a converted constant expression and the result value of the expression can be determined despite not being a constant expression.

So if example_function is not declared constexpr , then example_function() || true example_function() || true is not a constant expression because the evaluation would require calling the function. But true || example_function() true || example_function() is a constant expression, since the evaluation would not call the function.

Your is_constexpr<T> is guaranteed to work, because any semantic constraint violation involving a template parameter inside a requires-expression does not make the program ill-formed, but just makes the requires-expression result value false ( [expr.prim.req]/6 ). In is_constexpr<example_function> , using the non-constant expression T() as a template argument to std::enable_if via the default template argument instantiated by ConstexprHelper<T> is such a semantic error, so the requires-expression has value false .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM