Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are non member static constexpr variables not implicitly inline?

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.

like image 573
NoSenseEtAl Avatar asked Jun 26 '18 21:06

NoSenseEtAl


People also ask

Are constexpr variables implicitly inline?

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

Is constexpr implicitly static?

constexpr is a compile time feature, where inline/static are runtime features. The meaning of constexpr is more restricted than static. The meaning is that given a particular input value the return value is always the same, and the copiler ought to be able to work it out completely during compilation.

Is constexpr inline by default?

Yes ([dcl. constexpr], §7.1. 5/2 in the C++11 standard): "constexpr functions and constexpr constructors are implicitly inline (7.1. 2)."

Why does constexpr need to be static?

A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.


2 Answers

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.

like image 100
T.C. Avatar answered Oct 06 '22 23:10

T.C.


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.

like image 43
Brian Bi Avatar answered Oct 06 '22 23:10

Brian Bi