What is constexpr in C++?

2020-06-01 03:36发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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.