Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a compiler forced to reject invalid constexpr?

#include <exception>

constexpr bool foo(bool x)
{
  return x ? true : throw std::exception();
}

int main()
{
  // 1) must never be compiled
  // static_assert(foo(false), "");

  // 2) must always be compiled?
  const bool x = foo(false);

  // 3) must never compile?
  constexpr bool y = foo(false);

  return 0;
}

I'm sure that (1) must lead to a compile error. I'm quite sure that (2) must not be rejected at compile time, though it will fail at runtime.

The interesting case is the constexpr variable (3). In this simple example, gcc and clang actually evaluate the expression, and will therefore reject the program. (Error message: y is not a constant expression).

Is every C++11 compiler forced to reject the program? What if foo(false) was replaced by a more complex expression?

I was suprised to find out that constexpr were not turing-complete, though it will be after a change in the specification: Is constexpr-based computation Turing complete?

Maybe this is related to my question. As far as I understand, the compiler is allowed to postpone the actual evaluation of the constexpr (3) in this example until runtime. But if constexpr are turing-complete, I find it hard to believe that the compiler can decide for all constexpr whether an exception will be thrown (which means that the constexpr is invalid).

like image 465
Philipp Claßen Avatar asked Oct 27 '12 21:10

Philipp Claßen


People also ask

Is constexpr guaranteed to be compile time?

A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.

Can constexpr return void?

constexpr functionsIts return type cannot be void , i.e., it must compute and return a value. It can only call other constexpr functions.

What is the point of constexpr?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

How do I know if a function is constexpr?

The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.


2 Answers

By my reading, yes, every compiler must complain about statement (3).

N3242 7.1.5 paragraph 9:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, every full-expression that appears in its initializer shall be a constant expression. Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization shall be one of those allowed in a constant expression (5.19).

I think of a constexpr object as "evaluated at compile time", and a constexpr function or constexpr constructor as "might be evaluated at compile time". A compiler must determine the semantic validity of statements like (3) at compile time. You could argue that the "evaluation" can still be done at run time, but checking for validity does most of that work anyway. Plus, the code could then continue to instantiate a template like Check<y>, which pretty much guarantees the compiler needs to figure out the value of y at compile-time.

This does mean you could write a diabolical program to make the compiler take a really long or infinite time. But I suspect that was already possible with operator-> tricks.

like image 169
aschepler Avatar answered Oct 16 '22 18:10

aschepler


I'm sure that (1) must lead to a compile error. I'm quite sure that (2) must not be rejected at compile time, though it will fail at runtime.

Correct. The throw part of the conditional operator is not a constant expression, and in (1), it's not unevaluated. For (2), foo is not forced to be evaluated at compile-time.

For (3), how would the compiler be allowed to post-pone evaluation? The constexpr decl-specifier forces foo to be evaluated at compile-time. It's basically the same as (1), initialization of y is a context where a constant expression is required.

§7.1.6 [dcl.constexpr] p9

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression. [...]

like image 45
Xeo Avatar answered Oct 16 '22 18:10

Xeo