29

I am trying to initialize a constexpr reference with no success. I tried

#include <iostream>

constexpr int& f(int& x) // can define functions returning constexpr references
{
    return x;
}

int main()
{
    constexpr int x{20};
    constexpr const int& z = x; // error here
}

but I'm getting a compile time error

error: constexpr variable 'z' must be initialized by a constant expression

Dropping the const results in

error: binding of reference to type 'int' to a value of type 'const int' drops qualifiers

even though I had the feeling that constexpr automatically implies const for variable declarations.

So my questions are:

  1. Are constexpr references ever useful? (i.e., "better" than const references)
  2. If yes, how can I effectively define them?

PS: I've seen a couple of questions related to mine, such as Which values can be assigned to a `constexpr` reference? , but I don't think they address my questions.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252

3 Answers3

30
  1. Are constexpr references ever useful? (i.e., "better" than const references)

They are guaranteed to be initiailized before the program starts, whereas a reference to const can be initialized during dynamic initialization, after the program starts running.

  1. If yes, how can I effectively define them?

A constexpr reference has to bind to a global, not a local variable (or more formally, it has to bind to something with static storage duration).

A reference is conceptually equivalent to taking the address of the variable, and the address of a local variable is not a constant (even in main which can only be called once and so its local variables are only initialized once).

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Ahh, ok, thanks! It works if I move `x` outside `main()`. However, why do I need to put the extra `const` in the definition of `z`? – vsoftco Feb 19 '15 at 19:00
  • 9
    They need to bind to something with static storage duration, which can be a local `static` or a global. – T.C. Feb 19 '15 at 19:01
  • 3
    `constexpr` affects the reference, not the type it binds to, so to bind to a `const int` you still need a `const int&` – Jonathan Wakely Feb 19 '15 at 19:02
  • I'm not even sure that main can only be called once. Though it would certainly be highly unconventional, I don't think the standard forbids calling main like it was just another function. – antred Feb 19 '15 at 19:03
  • 5
    @antred [basic.start.main]/3: "The function `main` shall not be used within a program. The linkage of `main` is implementation-defined. A program that defines `main` as deleted or that declares main to be `inline`, `static`, or `constexpr` is illformed. The name `main` is not otherwise reserved." – Casey Feb 19 '15 at 19:05
  • 1
    @JonathanWakely so I guess the same apply for pointers, i.e. `constexpr int* p;` implicitly makes the type of the pointer `int* const`. – vsoftco Feb 19 '15 at 19:12
  • 2
    @vsoftco From a language point of view, `constexpr` is a *decl-specifier*. I'm not going to go into detail, but you can put the rest together. –  Feb 19 '15 at 19:17
  • @Casey Interesting, I stand corrected. :o Does the same rule apply for C, too? Because I recall a discussion on a C forum where a user had a recursive main function and claimed it was legal C. Not having found any evidence to the contrary, I accepted it and assumed that it was true for C++ as well. – antred Feb 19 '15 at 19:17
  • 1
    @antred C and C++ are different languages with different standards so there are many things that may be legal in C that are not legal in C++. – Shafik Yaghmour Feb 19 '15 at 19:19
  • 4
    @antred I'm no C expert, but a quick look at N1570 shows no similar restrictions. 5.1.2.2.3/1 Program Termination does say "If the return type of the `main` function is a type compatible with `int`, a return from the *initial* call to the `main` function is equivalent to calling the `exit` function with the value returned by the `main` function as its argument..." [emphasis added]. This would seem to imply that *non-initial* calls to main are compliant. – Casey Feb 19 '15 at 20:12
  • 3
    @T.C. *"which can be a local `static` or a global"* or a temporary whose lifetime is extended to static storage duration: `static constexpr int const& x = 42;` is fine. That's also why `static constexpr auto x = {1,2};` is accepted by clang++, but `constexpr auto x = {1,2};` is not (at block scope). (This implies that even `static constexpr const int& z = +x;` in the OP's code is legal.) – dyp Feb 19 '15 at 20:32
11

So the problem is that a constexpr reference needs to bind to an object with static storage duration, which is covered in the draft C++11 standard: N3337 section 5.19 [expr.const] (emphasis mine):

A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function

The draft C++14 standard: N3936 changes the wording:

A constant expression is either a glvalue core constant expression whose value refers to an object with static storage duration or to a function, or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

  • each non-static data member of reference type refers to an object with static storage duration or to a function, and
  • if the object or subobject is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.

So changing the declaration of x like so would work:

constexpr static int x{20};
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
5

Like T.C. says, the initializer needs to be an object with static storage duration.

N4140/§5.19/4 A constant expression is either a glvalue core constant expression whose value refers to an object with static storage duration [...]

N4140/§7.1.5/9 A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. [...] Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression.

In N3337, the wording is different.