Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference initialization and constant expressions

As a followup to this question, gcc and clang both consider this program ill-formed:

int main() {
    const int& ri = 0;
    constexpr int i = ri;
}

The error is about the value of ri being unusable in a constant expression. 0 is certainly a core constant expression, and as a prvalue core constant expression seems to satisfy these constraints (trivially, since int isn't of class, pointer, or array type). So shouldn't ri satisfy this criteria?

The same is true if I use a prvalue literal of class type:

struct X { };
int main() {
    const X& rx = X{};
    constexpr X x = rx;
}
like image 339
Barry Avatar asked Dec 20 '17 17:12

Barry


People also ask

How do you initialize const and reference member variables?

To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.

How do you initialize a reference variable?

If an rvalue reference or a nonvolatile const lvalue reference r to type T is to be initialized by the expression e , and T is reference-compatible with U , reference r can be initialized by expression e and bound directly to e or a base class subobject of e unless T is an inaccessible or ambiguous base class of U .

What is constant initialization in C++?

A constant variable must be initialized at its declaration. To declare a constant variable in C++, the keyword const is written before the variable's data type. Constant variables can be declared for any data types, such as int , double , char , or string .


2 Answers

In this statement:

const int& ri = 0;

0 is a prvalue, but ri isn't initialized from that prvalue. The prvalue first undergoes a temporary materialization conversion and the reference is bound to the resulting glvalue. Since ri is bound to this materialized glvalue, and not directly to the prvalue like you (I) suspected, the relevant restrictions are not the prvalue core constant expression restrictions (which 0 does satisfy) but rather the glvalue core constant expression restrictions - that the entity be a permitted result of a constant expression. That restriction, spelled with slightly improved clarity, is:

either an object with static storage duration that is:

  • not a temporary object, or
  • a temporary object whose value satisfies the above constraints,

or it is a function.

Our glvalue is a temporary object whose value satisfies "the above constraints" ("above" here referring to the prvalue core constant restrictions, which int trivially satisfies), but it does not have static storage duration.

Not having static storage duration → the entity is not a permitted result of a constant expression → the glvalue expression not a constant expression → ri wasn't initialized with a constant expression → ri can't be used in a core constant expression → the declaration of i is ill-formed.

The same argument holds for appropriate class types as well.

like image 165
Barry Avatar answered Oct 20 '22 10:10

Barry


As you pointed out, 2.11 states that a core constant expression must not evaluate to:

  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either

    • it is initialized with a constant expression or

And further expr.const#6:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

...

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

From my reading of this, it means that the RHS of const X& r = (substitute X for some type) must either be an object with static storage duration or a temporary object that meets the above criteria. Since an int does not fit either an object of class type, a pointer type or class/array type, it does not qualify.

like image 40
user9123744 Avatar answered Oct 20 '22 08:10

user9123744