constexpr defining static data member of literal t

2019-03-04 18:13发布

问题:

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?

回答1:

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)



回答2:

[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.