It is known in C++ that when initializing a reference, types must match. So the following code snippet causes an error (in global scope, the same below):
unsigned i;
int & p = i; // error
But there is an exception for reference to const, so the following is correct where a type conversion is allowed:
unsigned i;
const int & p = i; // ok
But if I define p
as constexpr
, a compiling error occurs:
unsigned i;
constexpr const int & p = i; // error: the value of 'i' is not usable in a constant expression
The error message says that i
is not a const, which I think is irrelevant, because if I change the type from signed to unsigned, i.e., no type conversion, there is no compiling error:
unsigned i;
constexpr const unsigned & p = i; // ok
const
can be omitted here.
So, it seems that it is the type conversion that caused the compiling error in
unsigned i;
constexpr const int & p = i; // error
But I am not sure. Can you please let me know what C++ standard rule caused this error? I tested the above codes in both GCC and MSVC, under C++20.
This part:
unsigned i;
int& p = i;
... cannot be compiled because it requires to bind an lvalue reference to a temporary which is materialised when converting unsigned
to int
. You can refer to dcl.init.ref#5.2 for details:
Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.
At the same time C++ allows binding a const
reference to such a temporary, so this works fine:
unsigned i;
const int& p = i;
This part is covered by dcl.init.ref#5.4.2:
Otherwise, the initializer expression is implicitly converted to a prvalue of type
T1
. The temporary materialization conversion is applied, considering the type of the prvalue to becv1 T1
, and the reference is bound to the result.
When using constexpr
however, you are supposed to provide a constant expression, which in this case is not satisfied:
unsigned i;
constexpr const int& p = i;
If using references in constant expressions, they are expected be bound directly as per expr.const#14:
A converted constant expression of type
T
is an expression, implicitly converted to typeT
, where the converted expression is a constant expression and the implicit conversion sequence contains only...
and where the reference binding (if any) binds directly.
And the reference binding cannot be direct when an implicit conversion takes place, as per dcl.init.ref#5:
In all cases except the last (i.e., implicitly converting the initializer expression to the referenced type), the reference is said to bind directly to the initializer expression.
But it's still OK to bind the constant expression reference to a global variable if no temporary is involved, so this compiles fine:
unsigned i;
constexpr const unsigned& p = i;