Say I initialize variables like this:
#include <cstdint>
constexpr uint16_t a = 65535;
constinit int64_t b = a * a; // warning: integer overflow in expression of type 'int' results in '-131071' [-Woverflow]
constexpr int64_t c = a * a; // error: overflow in constant expression [-fpermissive]
Both b
and c
produce undefined behavior because of integer overflow.
With constinit
the variable is constant initialized. Which makes no guarantee about UB.
With constexpr
the variable is initialized with a constant expression. Constant expression guarantee not to have any UB. So here the signed integer overflow in an error. But the variable is also automatically const.
So how do I best initialize a non-const variable with a constant expression?
Do I have to write
constexpr int64_t t = a * a; // error: overflow in constant expression [-fpermissive]
constinit int64_t b = t;
or
constinit int64_t b = []()consteval{ return a * a; }(); // error: overflow in constant expression
every time?
This is related to CWG issue 2543.
As it stands currently, because the compiler is allowed to replace any dynamic initialization with static initialization if it can and because
constinit
is only specified to enforce "no dynamic initialization", it might still allow an initializer which is not a constant expression (maybe dependent on the interpretation as discussed in the linked issue).constinit
therefore reflects whether there will actually be initialization at runtime (which is relevant to avoiding dynamic initialization order issues). It does not necessarily reflect whether the initializer is a constant expression.As stated in the issue description, this is practically not really implementable though because the dynamic/static initialization choice is made too late in the compilation process to always make
constinit
reflect it properly.With one possible resolution of the issue, the specification of
constinit
might be changed to actually require the variable to be constant-initialized instead of just requiring that there is no dynamic initialization. If that was the resolution taken, then your first example for the initialization ofb
would also require the compiler to diagnose the UB and all of the other solutions would become obsolete.The issue description doesn't seem to really favor any direction though.
For the current situation (and if the resolution is taken in another direction), an alternative to the solutions you gave is:
or
and then
or
Note that you need to include the target type in this way in the initializer, otherwise any potentially UB in the conversion will not be diagnosed. If you don't care about that
would also be fine.
Technically the solution with the
consteval
lambda from your question is ill-formed, no diagnostic required, because the lambda is markedconsteval
but can never produce a constant expression when called. But I would expect any non-malicious compiler to still diagnose such a call.