I have a question about constexpr defining a static data member of literal type that is declared const (and not specified inline or constexpr) in the class definition:
// S.h
struct S
{
static int const i; // not specified inline or constexpr
};
// S.cpp
#include "S.h"
constexpr int const S::i = 42; // definition, not declaration
// main.cpp
#include "S.h"
int main()
{
return S::i;
}
Clang/gcc return 42 in C++11/14 mode, but report an error (undefined reference to S::i) in C++17 mode. If I comment out constexpr both return 42 in C++17 mode, too.
S::i has external linkage because S has external linkage. S::i is not declared constexpr and so (if I'm not mistaken) C++17 10.1.5 p1 does not apply:
A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable
I understand this sentence as if it means (bold my understanding):
A static data member declared with the constexpr specifier in the class definition is implicitly an inline variable
S::i is thus not an inline variable.
Yet the definition of S::i seems to have internal linkage in C++17 mode as if constexpr means inline. Is this correct? If so where is the proof in the standard?
Or do I misunderstand 10.1.5 p1 and it really means (bold my misunderstanding):
A static data member declared with the constexpr specifier in the class definition and the definition in namespace scope is implicitly an inline variable?
Yet the definition of S::i seems to have internal linkage in C++17
mode as if constexpr means inline. Is this correct? If so where is the
proof in the standard?
Yes, it is correct. cppreference :
The inline specifier, when used in a decl-specifier-seq of a variable
with static storage duration (static class member or namespace-scope
variable), declares the variable to be an inline variable.
A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable. (since C++17)
[dcl.inline]/6 states:
If a function or variable with external linkage is declared inline in
one translation unit, it shall be declared inline in all translation
units in which it appears; no diagnostic is required.
So as you pointed out, if we can show that constexpr
implicitly implies inline
, it would explain the undefined reference error of your example.
[dcl.constexpr]/1 states [emphasis mine]:
The constexpr specifier shall be applied only to the definition of
a variable or variable template or the declaration of a function or
function template.
As well as:
A function or static data member declared with the constexpr specifier
is implicitly an inline function or variable ([dcl.inline]).
[basic.def]/1 states [emphasis mine]:
A declaration may introduce one or more names into a translation unit
or redeclare names introduced by previous declarations.
as well as (/2):
A declaration is a definition unless:
[... none applies for constexpr int const S::i = 42;
]
The essence here being that definitions are declarations (that fully define the entity introduced by the declaration), so constexpr int const S::i = 42;
is also (in addition to being a definition) a (re-)declaration, in which case [dcl.constexpr]/1 applies, and S::i
is inline in the translation unit of S.cpp
, ergo, by [dcl.inline]/6, in all other translations units in which it appears. Conversely, by [dcl.constexpr]/1, the constexpr
specifier, e.g. specifically in this context of static data members, can only appear in declarations that are definitions.
Somewhat relevant in the context of the latter is that a constexpr
static data member declaration with initialization is, also, as of C++17, a definition, allowing for the specification that constexpr
shall only be applied to the variable definition (i.e., never to a non-initializing declaration). See [depr.static_constexpr]/1.