0

Just learnt about constexpr functions and went to godbolt to know how compiler optimize code and found out, that compilers act quite differently.

Have the following code:

constexpr int square(int num) {
    return num * num;
}

int main() {
    int a = square(2);
}

g++ and icc compilers calculate function result and assign it to a variable (as I expected), but msvc and clang call function.

To use optimization we should make additional step:

constexpr int square(int num) {
    return num * num;
}

int main() {
    constexpr int c = square(2);
    int a = c;
}

Is there any reasonable explanation of such behavior?

Link to godbolt example: https://godbolt.org/z/ez7luu

max66
  • 65,235
  • 10
  • 71
  • 111
Denis Sablukov
  • 3,360
  • 2
  • 26
  • 31

1 Answers1

3

All compilers are correct.

A constexpr function is a function that can be computed compile-time or run-time, according to the circumstances.

Pretending there isn't the as-if rule, we can say that the compiler must compute compile-time when the result of a constexpr function goes somewhere where is requested to be known compile time.

By example, the size of an array

int  a[square(10)];

or a template parameter

std::array<int, square(10)>  a;

or a constexpr variable

constexpr int  a { square(10) };

There are circumstances where the function must be computed run-time, as when receive run-time known input values; by example

int a;

std::cin >> a;

int b { square(a) }; 

Otherwise the compiler can choose if compute the value compile-time or run-time.

In your first version

int a = square(2);

we are in the compiler-can-choose area, because 2 is known compile-time, so the compiler can choose the compile-time computation, but the value is requested for a not constexpr variable, so a compile-time value isn't necessary.

And you see that two compilers are computing compile-time and two others run-time. Generally, this kind of behavior heavily depends on the optimization level. In fact, all compilers produce a different output after adding -O2 to the compile flags in your example.

In your second version

 constexpr int c = square(2);

the square() value is requested for a constexpr variable, so all compilers must compute square(2) compile-time (and they can do it, because 2 is a value known compile-time).

Julius
  • 1,816
  • 10
  • 14
max66
  • 65,235
  • 10
  • 71
  • 111