C++: constexpr implying implicit const to variables does not apply to reference

Viewed 172

There has been lots of questions/answers pertaining questions to constexpr expression but i have a question which is pretty close to other question but slightly different in another sense. Anyway here it goes.

#include <iostream>
using namespace std;
                       
constexpr int x = 1; // TAG A

int main() {
   constexpr int &xf = x; // TAG B error out
   const int &xf1 = x; // TAG C works
   constexpr int const &xf2 = x; // TAG D works
   return 0;
}

Error:

Binding reference of type 'int' to value of type 'const int' drops 'const' qualifier [reference_bind_drops_quals]

Comments:

  1. TAG A clearly shows x is const int. Accordingly to the C++ Primer 5th Edition on page section 2.44 "Variables declared as constexpr are implicitly const and must be initialized by constant expressions:". So this is good.
  2. TAG B --> error message implies that the constexpr did not implicitly "const" the variable xf. Hence xf is int &. This contradict TAG A. Why?
  3. TAG C --> This is the usual reference to const
  4. TAG D --> Additional "const" added to make it a const reference due to how it behaves in TAG B.

Is there a difference in the word "reference to a const" and "const reference"?
it seems reference to const means reference is referring to a const object while not allowing to make modification while "const reference" is that the reference is const but it can point to either const or non-const objects.

This is one big confusing thing.

2 Answers

The constexpr in B refers to the variable xf, not the type (int & constexpr xf;, although I don't know if that will compile). The variable xf is a constexpr and it has type int &. This is why trying to bind it to x fails.

Is there a difference in the word "reference to a const" and "const reference"

Yes, conceptually, but "const reference" logically collapses to just "reference" because technically all references are constant. This is also why the language doesn't let you declare references as explicitly const. That said, there are still cases in the language where it could make sense to talk about a "const reference". Consider the following example:

typedef int &intref;

int main() {
    int x = 0;
    const intref xf = x;
    return 0;
}

In this case, xf is in fact a const reference (not a reference to const), and the declaration is still valid. This will give you a warning about the const qualifier being ignored, but it will still compile and run. This illustrates why it's still important to be extremely rigorous about sticking to declaration order rules.

The next very important piece of information to understand for this declaration is the application of the constexpr keyword, and exactly what it means when we say it "implies const". The constexpr keyword always refers to the object in the declaration (not the type), so when we say constexpr implies const, it means in a hand-wavy sense, you can omit the const keyword on the inner most part of a constexpr declaration (xf in this example) because it's redundant. In the special case of a reference, it's not only redundant, it's not allowed. You could argue that the compiler throwing an error is a little bit unnecessary given that you can get around it with a typedef, but the principle still applies, as does the rule for the application of the missing const.

Putting all this together, in your example, you can read declaration B out loud as something like:

"xf is a constexpr which is a reference (which is by definition const, and therefore no need to imply const) to an integer."

But here we have a problem, in that we're trying to declare a reference to a non-constant object and assign it to a constant object. This is exactly what the compiler complains about.

See also this.

Related