2016-06-14 13 views
11

Rozważmy następujący KOD:Inicjowanie statycznego elementu danych constexpr klasy bazowej za pomocą statycznego elementu danych constexpr klasy uzyskanego

template<typename T> 
struct S { static constexpr int bar = T::foo; }; 

struct U: S<U> { static constexpr int foo = 42; }; 

int main() { } 

GCC v6.1 kompiluje, clang 3.8 odrzuca się z powodu błędu:

2 : error: no member named 'foo' in 'U'
struct S { static constexpr int bar = T::foo; };

Który kompilator ma rację?
Czy przyczyną może być to, że Uis not a complete type w punkcie, w którym próbujemy go użyć w ramach S?
W tym przypadku, należy uznać za błąd z GCC, ale chciałbym wiedzieć, czy mam rację przed szukać/otworzyć problem na bug tracker ...

EDIT

Tymczasem otworzyłem bug na GCC.
Oczekiwanie na akceptację odpowiedzi.

+1

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

+0

@bogdan Wow, dziękuję bardzo. Naprawdę doceniony. – skypjack

+0

Odpowiedź zaktualizowana. – bogdan

Odpowiedz

5

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.

Powiązane problemy