Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can a `constexpr` function produce different results at compile- and run-time?

A colleague of mine showed me this shocking C++20 program:

#include <iostream>

constexpr int p(auto) { return 0; }
constexpr int q() { return p(0); }
constexpr int p(auto) requires true { return 1; }

static_assert(p(0) == 1);
static_assert(q() == 0);

int main()
{
    std::cout << q() << p(0) << '\n';
}

GCC cannot build it due to the error:

Error: symbol `_Z1pIiEiT_' is already defined

Clang builds the program successfully and prints 11( https://gcc.godbolt.org/z/1Gf5vj5oo ). So static_assert(q() == 0) was successfully checked, but std::cout << q() still printed 1. How can this be?

Visual Studio 2019 16.10.4 behaves even more weirdly. In Release configuration it prints also 11, and in Debug configuration it prints 00. And here in both cases run-time values of functions differ from their compile-time values, verified by static_assert.

The only explanation I can think of is that all these are compiler bugs, and a constexpr function must always produce the same result at compile- and run-time. Is that right?

like image 783
Fedor Avatar asked Aug 01 '21 11:08

Fedor


People also ask

Is constexpr always compile time?

A constexpr variable must be initialized at compile time. All constexpr variables are const . A variable can be declared with constexpr , when it has a literal type and is initialized.

Can constexpr variable be changed?

Both const and constexpr mean that their values can't be changed after their initialization. So for example: const int x1=10; constexpr int x2=10; x1=20; // ERROR. Variable 'x1' can't be changed.

Why does constexpr need to be static?

A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.

Is constexpr always inline?

A constexpr function can be executed at compiletime. The only types usable within a constexpr context are literal types. A constant expression or constexpr function may also not invoke a non constexpr function. The constexpr keyword implies inline.

Are constexpr functions always evaluated at compile time?

@Herb Sutter: The general expectation seems to be that constexpr functions are always evaluated at compile time. Cedric's comment is a clear example of that, he says "This is all about optimization". And hence expect that constexpr functions are always evaluated at compile time, regardless of its context. To me, they are all about expressiveness...

What is a constexpr function in Python?

A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument. When its arguments are constexpr values, a constexpr function produces a compile-time constant.

What is the difference between constexpr and constexpr?

Unlike const, constexpr can also be applied to functions and class constructors. 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.

What is not initialized in constexpr?

Not initialized int j = 0; constexpr int k = j + 1; //Error! j not a constant expression A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.


1 Answers

While this program is contrived, it is valid and does what you think (prints “01”), so all compilers are wrong. GCC is failing to mangle the requires true into the name of the second p, MSVC/Debug is failing to select that more-constrained overload, and the other two cases are failing to use the lookup result from q (which is not itself a template subject to multiple points of instantiation).

As for the question title, std::is_constant_evaluated does allow constant evaluation to produce different results from runtime evaluation. Use this power only for good!

like image 115
Davis Herring Avatar answered Sep 27 '22 21:09

Davis Herring