Dla C++ 14 i 11 Clang ma rację; jednak wszystko uległo zmianie w najnowszej wersji roboczej (przyszłe C++ 17) - patrz następna sekcja.
Standard cytuje szukać są (z N4140, projekt najbliżej C++, 14):
[temp.inst]/1:
[...] The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members and member templates; [...]
[temp.point]/4:
For a class template specialization, [...] the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
więc punkt instancji dla S<U>
jest tuż przed deklaracją U
, tylko z przodu deklaracji struct U;
koncepcyjnie włożonej wcześniej, tak że nazwa U
została znaleziona.
[class.static.data]/3:
[...] A static data member of literal type can be declared in the class definition with the constexpr
specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [...] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
Według cytowanego wyżej ustępu deklaracji bar
w definicji S
, mimo że ma inicjatora, jest wciąż tylko deklaracja nie jest definicją, więc jest tworzona, gdy S<U>
jest niejawnie utworzona i nie ma w tym czasie wartości U::foo
.
Obejście problemu polega na tym, że funkcja bar
jest funkcją; zgodnie z pierwszym cytatem, definicja funkcji nie zostanie utworzona w momencie domyślnego utworzenia S<U>
. Tak długo jak używasz bar
po zobaczeniu definicji U
(lub z wnętrza innych funkcji składowych S
, ponieważ te z kolei będą tworzone tylko w razie potrzeby - [14.6.4.1p1]), coś jak to będzie działać:
template<class T> struct S
{
static constexpr int bar() { return T::foo; }
};
struct U : S<U> { static constexpr int foo = 42; };
int main()
{
constexpr int b = U::bar();
static_assert(b == 42, "oops");
}
po przyjęciu P0386R2 do projektu roboczego (obecnie N4606) [class.static.dane]/3 zostały zmienione; odpowiednia część brzmi teraz:
[...] An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr
specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). [...]
ta jest uzupełniona przez zmianę [basic.def] /2.3:
A declaration is a definition unless:
[...]
- it declares a non-inline static data member in a class definition (9.2, 9.2.3),
[...]
Tak więc, jeśli to inline, to definicja (z lub bez inicjatora). I [dcl.constexpr]/1 mówi:
[...] A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable (7.1.6). [...]
Co oznacza deklaracja bar
jest obecnie definicja, i zgodnie z cytatami w poprzedniej części nie jest to instancja dla niejawnego instancji z S<U>
; tylko deklaracja bar
, która nie zawiera inicjalizatora, jest tworzona w tym czasie.
Zmiany w tym przypadku są ładnie przedstawione w przykładzie z [depr.static_constexpr] w obecnej wersji roboczej:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
const int A::n; // redundant declaration (definition in C++ 2014)
To sprawia, że zachowanie standardowej conformant w C++ 1z trybie GCC.
Najnowszy partia aktualizacji do standardowej wersji roboczej obejmują zmiany [9.2.3.2p3] związane z nową koncepcją * zmiennych inline * ('zmienne constexpr' są teraz domyślnie * * inline, podobnie jak funkcje), tak odpowiedź na C++ 17 może się zmienić; aktualny nadal obowiązuje w C++ 14 i poniżej. Poczekam do najnowszej wersji spec zostać opublikowane w Dzienniku mailingu i wtedy będę aktualizować odpowiedź z C++ 17-konkretnych informacji. – bogdan
@bogdan Wow, dziękuję bardzo. Naprawdę doceniony. – skypjack
Odpowiedź zaktualizowana. – bogdan