The constructor uses a function taking a reference and returning by value while repeatedly modifying a data member:
constexpr int vv(int x) {return x;}
constexpr int & rr(int & x) {return x;}
constexpr int rv(int & x) {return x;}
constexpr struct S {
int x {0};
template<typename F> constexpr S(F f) {x = f(x) + 1; x = f(x) + 1;}
} s(rv); // s.x is 1 if function rv is used, 2 otherwise.
static_assert(s.x == 2, "");
It is only the function rv
which gives the unexpected result when used in the constructor. If vv
or rr
is passed instead then s.x
is 2 as expected.
I noticed the behavior on GCC 5.4.1 ARM and it appears to be the same in all versions of GCC that support C++14. Clang gives the expected result of 2 in all cases. Neither GCC nor Clang give any warnings with Wall and Wextra enabled.
Is this example valid C++14 and a bug in GCC? I read the list of restrictions on constant expressions in the standard and see nothing obvious it violates but I'm not sure I understand all the technical details.
Your code is certainly intended to be well-formed. I believe GCC performs a revised form of memoization; back in C++11, objects could not be modified in constant expressions, hence it was perfectly fine to cache function results. In fact, it's quite apparent from a few Clang bug reports that GCC did exactly that, see e.g. here:
Clang's constexpr implementation does not perform caching. In C++14, it's not even clear whether caching will be possible, so it seems like a waste of time to invest in it now.
They obviously had to introduce some additional analysis in C++14, and they made a mistake: they assumed that any constexpr
object's subobject cannot be modified during its lifetime. That clearly doesn't hold during the period of construction of the enclosing object. If we use a temporary instead of x
, the code compiles. If we put the whole thing into a constexpr
function and remove s's constexpr
specifier, it works.
Funnily enough, rr
's and vv
's results are also cached, but the same reference being returned works fine, and call by value avoids that problem altogether :)
Reported as 79520.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With