24

I am really confused about a constexpr concept, as I have read constexpr is evaluated at compile time, so it is useful for performance optimization versus normal const.

constexpr int i = 0;
constexpr int& ri = i;

The above code returns an error "invalid initialization of reference of type 'int&' from expression of type 'const int'", why?

Also, the next code has an error:

constexpr int i = 0;
constexpr int* ri = &i;

If I replaced the constexpr keyword with const, all above worked correctly.

Cee McSharpface
  • 8,493
  • 3
  • 36
  • 77
Bassam Najeeb
  • 607
  • 2
  • 7
  • 16
  • 1
    Possible duplicate: http://stackoverflow.com/questions/13346879/const-vs-constexpr-on-variables – πάντα ῥεῖ Feb 08 '17 at 08:11
  • 1
    you forgot const qualifier `constexpr int const& ri = i;`. `constexpr int i = 0;` also declare `i` as const... [demo](http://melpon.org/wandbox/permlink/2oc2VMdtymZ93ANa) – W.F. Feb 08 '17 at 08:11
  • Possible duplicate of [const vs constexpr on variables](http://stackoverflow.com/questions/13346879/const-vs-constexpr-on-variables) – Swapnil Feb 08 '17 at 08:13
  • 1
    "if I replaced the constexpr word with const all above worked correctly." A `const int*` essentially means `(const int)*` (except you can't use parentheses that way). A `constexpr int*` means `constepxr (int*)` (ditto note). – Cheers and hth. - Alf Feb 08 '17 at 08:17

4 Answers4

14
constexpr int i = 0;
constexpr int  * ri = &i;

The second line is a problem because the pointer does not point to a const object. The pointer itself is const.

Using

constexpr int i = 0;
constexpr int const * ri = &i;

solves that problem. However, that will be still a problem if the variables are defined in a function scope.

constexpr int i = 0;
constexpr int const* ri = &i;

int main() {}

is a valid program.

void foo()
{
   constexpr int i = 0;
   constexpr int const* ri = &i;
}

int main() {}

is not a valid program.

Here's what the C++11 standard has to say about address constant expression:

5.19 Constant expressions

3 .. An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I'm not familiar with the function scope problem. I see that g++ behaves as you state, but MSVC accepts that allegedly invalid code. g++ says that `i` isn't constant, but allows its use as array size (and not a VLA). That is inconsistent. g++ accepts the code with `static` added. Are you sure that you're not reporting a compiler quirk (bug) as a rule of the language? If it is a rule, what is it? – Cheers and hth. - Alf Feb 08 '17 at 08:28
  • @Cheersandhth.-Alf I think RSahu is right one cannot supply address that is runtime dependent to constexpr pointer. Imagine the situation when the foo is called recursively. `ri` should get different value at each recursion level, but how many recursion levels are there? It may depend on runtime value... – W.F. Feb 08 '17 at 08:33
  • @W.F.: I would not offhand think that argument would hold, because there's no sense in giving `i` a different adress determined at run time, but with a value known at compile time. That's just idiocy. If there is such a rule in the current language definition, then that's clearly a defect in the standard. So I'm interested in what the (alleged) rule is. – Cheers and hth. - Alf Feb 08 '17 at 08:36
  • @Cheersandhth.-Alf yup you're right I forgot that it's constexpr :) yes it is interesting indeed... – W.F. Feb 08 '17 at 08:38
  • @Cheersandhth.-Alf, Added a reference to the standard that corroborates my statement. – R Sahu Feb 08 '17 at 08:39
  • @RSahu: It could corroborate your statement if (and this would be a defect) a `constexpr` object does not always have static storage duration. So we need to look at that. But I need some breakfast, medicine and coffee. :) – Cheers and hth. - Alf Feb 08 '17 at 08:41
  • @Cheersandhth.-Alf, I didn't see anything in section 5.19 of the standard about the storage duration of `constexpr` objects. Whether that is a defect or not, I am not sure. – R Sahu Feb 08 '17 at 08:47
  • [this](http://stackoverflow.com/a/13867690/4324224) looks like a nice discussion on the static vs constexpr local objects... – W.F. Feb 08 '17 at 09:17
  • 1
    @RSahu: I've searched through the C++ 14 standard and perused google results such as (http://stackoverflow.com/a/38853356/464581), and it seems as you're right about the formal: a `constexpr` object needs not have static storage duration, and probably a local non-`static` one is required to behave *as-if* it's of automatic storage duration. This supports (1) `constexpr` objects that are not really constant because they have `mutable` parts, (2) per translation-unit optimization where an non-ODR used non-`static` `constexpr` object is removed, and (3) just wasting programmers' time. A defect. – Cheers and hth. - Alf Feb 08 '17 at 09:41
  • 2
    Thanks for noting this, I learned something new today. I thought that kind of nonsense only started with C++14 and bloomed with C++17. Non-constant `constepxr` objects. Grumble, grumble... Argh! – Cheers and hth. - Alf Feb 08 '17 at 09:43
8

Re

if I replaced the constexpr word with const all above worked correctly."

A const int* essentially means (const int)*, except that you can't use parentheses that way. A constexpr int* means constepxr (int*) (ditto note).

This is because constexpr is not part of the type, you can't name the type constexpr int, say, while const is part of the type.

Instead of

constexpr int i = 0;
constexpr int& ri = i;

which attempts to declare a constexpr reference to non-const, just write

constexpr int i = 0;
constexpr int const& ri = i;

You can read that backwards as ri is a reference to a const int which is constexpr (evaluated at compile time).


Addendum:

It ¹appears that C++14 requires local non-static constexpr objects to have automatic storage duration, modulo the as-if rule for optimization.

To cater for this, i.e. to make the code portable across compilers, if the above declarations appear locally in a function, add static to ensure static storage duration for the object that one refers to:

void oops()
{
    static constexpr int i = 0;      // Necessary with some compilers.
    constexpr int const& ri = i;
}

Otherwise it may not compile with e.g. g++, and it's probably what the C++14 and C++11 standards require, by omission of suitable constraints on constexpr.

Notes:
¹ See the discussion of R. Sahu's answer.

Community
  • 1
  • 1
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
5

As you said, constexpr is evaluated at compile time. So the value must be evaluable when compiling.

For example:

constexpr int i = 0;
constexpr int& ri = i;

For first line, 0 is evaluable when compiling, its value is 0.

But for second line, compiler needs address of i to do the assignment, which is determined at runtime. So this line will fail.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
zhm
  • 3,513
  • 3
  • 34
  • 55
  • Is a constexpr uses only for performance optimization versus a const? – Bassam Najeeb Feb 08 '17 at 08:24
  • 1
    @BassamNajeeb It can be used to replace macros, and hard-coded literals. For example, you can wrap macros and literals into `constexpr` functions, and provide a meaningful name for them. – zhm Feb 08 '17 at 08:30
1

Here are my 2 cents:

The constexpr feature defines computation that happens during the compile time. Reasonable question:

  • Does it make sense to allow any computation?

Reasonable answer:

  • No, because this would require a lot of machinery, resources, etc right inside the compiler while there is no direct need for that.

Because of that standard allows pretty limited set of features inside the constexpr code. One may argue why exactly this set and not something more? Well, later on the standard may evolve and allow more.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51