In C++17, are you allowed to modify global variables in a constexpr
function?
#include <iostream> int global = 0; constexpr int Foo(bool arg) { if (arg) { return 1; } return global++; } int main() { std::cout << global; Foo(true); std::cout << global; Foo(false); std::cout << global; }
I wouldn't expect you to be able to, but clang 6 allows it: https://godbolt.org/g/UB8iK2
GCC, however, doesn't: https://godbolt.org/g/ykAJMA
Which compiler is correct?
According to the proposal, an object created within a constexpr expression, can now be changed during the evaluation process - until the evaluation process or the object's lifetime ends.
const can only be used with non-static member functions whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types.
The constexpr keyword implies inline.
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.
I'll add that dcl.constexpr/5 additionally requires:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
Since you deiberately wrote the function so that Foo(true)
evaluates to a core constant expression, Foo(false)
is not required to.
Which compiler is correct?
Clang is right.
The definition of a constexpr
function as per dcl.constexpr/3
The definition of a
constexpr
function shall satisfy the following requirements:
(3.1) its return type shall be a literal type;
(3.2) each of its parameter types shall be a literal type;
(3.3) its function-body shall be= delete
,= default
, or a compound-statement that does not contain:
(3.3.1) an asm-definition,
(3.3.2) a goto statement,
(3.3.3) an identifier label,
(3.3.4) a try-block, or
(3.3.5) a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
Also as per dcl.constexpr/5:
For a
constexpr
function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression,
Foo(true)
could be evaluated to a core constant expression (i.e 1
).
Also, Foo(false)
could be but is not required to be constant evaluated.
CONCLUSION
Thus, a bug in GCC.
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