2016-06-14 15 views
11

den folgenden Code vor:ein statisches constexpr Datenelement der Basisklasse Initialisieren durch ein statisches constexpr Datenelement der abgeleiteten Klasse unter Verwendung

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 es kompiliert, verwirft clang 3.8 es mit dem Fehler:

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

Welcher Compiler ist richtig?
Könnte es aufgrund der Tatsache sein, dass Uis not a complete type an dem Punkt, wo wir versuchen, es innerhalb S zu verwenden?
In diesem Fall sollte es einen Fehler von GCC in Betracht gezogen werden, aber ich würde gerne wissen, ob ich direkt vor bin/öffne auf dem Bug-Tracker, ein Problem zu suchen ...

EDIT

Inzwischen habe ich eine bug zu GCC geöffnet.
Warten auf die Antwort zu akzeptieren.

+1

Der letzte Batch von Aktualisierungen des Standardarbeitsentwurfs enthält Änderungen an [9.2.3.2p3] bezogen auf das neue Konzept von * Inline-Variablen * ('constexpr' Variablen sind jetzt implizit * inline *, genau wie Funktionen) die Antwort für C++ 17 könnte sich ändern; der aktuelle gilt immer noch für C++ 14 und darunter. Ich warte darauf, dass die neueste Version der Spezifikation im offiziellen Mailing veröffentlicht wird, und dann aktualisiere ich die Antwort mit C++ 17-spezifischen Informationen. – bogdan

+0

@bogdan Wow, vielen Dank. Sehr geschätzt. – skypjack

+0

Antwort aktualisiert. – bogdan

Antwort

5

Für C++ 14 und 11 hat Clang Recht; Im letzten Arbeitsentwurf (dem zukünftigen C++ 17) hat sich jedoch einiges geändert - siehe nächsten Abschnitt.

The Standard suchen zitiert sind (von N4140, den Entwurf der Nähe von 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.

der Punkt der Instanziierung für S<U> so ist unmittelbar vor der Erklärung der U, wobei nur eine Vorwärtsdeklaration struct U; konzeptionell vor eingefügt, so dass die Name U wird gefunden.

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

Nach dem Absatz oben zitierte, die Erklärung von bar innerhalb der Definition von S, obwohl es einen Initialisierer hat, ist nach wie vor nur eine Erklärung , keine Definition, also wird es instanziiert, wenn S<U> implizit instanziiert wird und es zu dieser Zeit keine U::foo gibt.

Eine Abhilfe ist, bar eine Funktion zu machen; Gemäß dem ersten Zitat wird die Definition der Funktion zum Zeitpunkt der impliziten Instanziierung von S<U> nicht instanziiert. Solange Sie bar verwenden, nachdem die Definition von U gesehen wurde (oder aus den Körpern anderer Elementfunktionen von S, da diese wiederum nur bei Bedarf separat instanziiert werden - [14.6.4.1p1]), etwas wie dies funktionieren wird:

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"); 
} 

die Annahme P0386R2 in den Arbeitsentwurf verfolgt (derzeit N4606) [class.static.Daten]/3 wurde geändert; der relevante Teil liest jetzt:

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

Dies wird durch die Änderung ergänzt wird [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),

[...]

Also, wenn es inline ist, ist es eine Definition (mit oder ohne Initialisierer). Und [dcl.constexpr]/1 sagt:

[...] A function or static data member declared with the constexpr specifier is implicitly an inline function or variable (7.1.6). [...]

Was bedeutet die Erklärung von bar ist jetzt eine Definition, und nach den Zitaten im vorigen Abschnitt ist es nicht für die implizite Instanziierung S<U> instanziiert; Nur eine Deklaration von bar, die den Initialisierer nicht enthält, wird zu dieser Zeit instanziiert.

Die Änderungen in diesem Fall sind in dem Beispiel in [depr.static_constexpr] im aktuellen Arbeitsentwurf schön zusammengefasst:

struct A { 
    static constexpr int n = 5; // definition (declaration in C++ 2014) 
}; 

const int A::n; // redundant declaration (definition in C++ 2014) 

Dies macht in C++ 1z Modus Verhalten Standard-konformer des GCC.