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).