11

In C++17 we got inline variables and I have assumed that global constexpr variables are implicitly inline. But apparently this is true only for static member variables.

What is the logic/technical limitation behind this?

source:

A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

2 Answers2

10

The reason why constexpr static data members were made implicitly inline was to solve a common problem in C++: when defining a class-scoped constant, one was previously forced to emit the definition in exactly one translation unit, lest the variable be ODR-used:

// foo.h
struct foo {
    static constexpr int kAnswer = 42;
};

// foo.cpp
// a linker error will occur if this definition is omitted before C++17
#include "foo.h"
constexpr int foo::kAnswer;

// main.cpp
#include "foo.h"
#include <vector>
int main() {
    std::vector<int> bar;
    bar.push_back(foo::kAnswer);  // ODR-use of 42
}

In such cases, we usually care only about the value of the constant, not its address; and it's convenient for the compiler to synthesize a unique location for the constant in case it really is ODR-used, but we don't care where that location is.

Thus, C++17 changed the rules so that the out-of-line definition is no longer required. In order to do so, it makes the declaration of foo::kAnswer an inline definition, so that it can appear in multiple translation units without clashing, just like inline functions.

For namespace-scope constexpr variables (which are implicitly static, and therefore have internal linkage, unless declared extern) there is no similar issue. Each translation unit has its own copy. inline, as it's currently specified, would have no effect on such variables. And changing the existing behaviour would break existing programs.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • OK, but then why global constexpr statics can be inline? What is the point in that? – NoSenseEtAl Jun 26 '18 at 22:37
  • 1
    @Brian, you can declare static constexpr primitive types in header files without having to declare them in .cpp file. They are not ODR used. Your example says is only after C++17, but that's not true. It works for C++11 and 14. – AdvSphere Jun 26 '18 at 23:07
  • @AdvSphere Only if they're namespace-scoped. Not class members. – Brian Bi Jul 09 '18 at 21:08
  • @Brian, not sure if I'm missing something from what you are saying, but I have a code that compiles successfully where there's a header file that declares a class, and inside the class scope defines `static constexpr double value {1.5};`. The header is included in multiple translation units and it works. Are we talking about different things? – AdvSphere Jul 09 '18 at 21:34
  • @AdvSphere It only works if you don't accidentally ODR-use the member, such as by calling `std::vector::push_back(value)`. – Brian Bi Jul 09 '18 at 21:38
  • @Brian, I tested and you are right!! thank you for the clarification, I learned something new :). Could you please explain to me how come it wouldn't work? The way I see it is the `push_back(value)`, value is a prvalue where the compiler can assign a temporary (xvalue) in order to copy the value, this is temporary materialization for prvalues. – AdvSphere Jul 09 '18 at 21:52
  • 1
    @Brian, your above answer to OP says it all, thank you again! – AdvSphere Jul 09 '18 at 22:29
  • 1
    I see a lot of people gave you upvotes, but for me this does not answer my question. In particular last part of you answer is contradictory: "Each translation unit has its own copy. inline, as it's currently specified, would have no effect on such variables. And changing the existing behaviour would break existing programs." So we can not change it to not break old code, but it is would make no difference? – NoSenseEtAl Jul 10 '18 at 13:17
10

The point here is that constexpr int x = 1; at namespace scope has internal linkage in C++14.

If you make it implicitly inline without changing the internal linkage part, the change would have no effect, because the internal linkage means that it can't be defined in other translation units anyway. And it harms teachability, because we want things like inline constexpr int x = 1; to get external linkage by default (the whole point of inline, after all, is to permit the same variable to be defined in multiple translation units).

If you make it implicitly inline with external linkage, then you break existing code:

// TU1
constexpr int x = 1;

// TU2
constexpr int x = 2;

This perfectly valid C++14 would become an ODR violation.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • does an `inline constexpr` variable at namespace scope have implicit external linkage? constexpr in this context implies const. and const implies static linkage (in namespace scope). Or is this a special extra rule? under internal linkage on: https://en.cppreference.com/w/cpp/language/storage_duration inline variables are explicitly excluded from the list ("non-inline (since C++17) const-qualified variables (including constexpr) that aren't declared extern and aren't previously declared to have external linkage"). so they are excluded from internal linkage -> they have external linkage? – phön Jul 27 '18 at 09:56