2016-12-21 1 views
17

Zum Beispiel:Wie sollte eine Variablenvorlage in C++ 14 bezeichnet werden, wenn sie im Klassenbereich deklariert wird?

class example{ 
    public: 
     template <class T> static constexpr T var = T(1.5); 
}; 

int main(){ 

    int a = example::var<int>; 

    example obj; 
    int b = obj.var<int>; 

    return 0; 
} 

GCC produziert Fehler sowohl für: 'example::var<T>' is not a function template und 'var' is not a member template function

Clang korrekt die erste kompiliert, sondern ein Fehler für die zweite produziert: cannot refer to member 'var' in 'example' with '.'

Gemäß der C + +14 Standard (ISO/IEC 14882: 2014):

Abschnitt 14, Absatz 1.

A variable template at class scope is a static data member template.

Abschnitt 9.4, Absatz 2.

A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred to using the class member access syntax, in which case the object expression is evaluated.

Daher IMHO, eine Variable Vorlage bei der Klasse Rahmen (das heißt ein Static Data Mitglied Template) könnte in beiden Richtungen bezeichnet. Könnte es ein Fehler in den Compilern sein?

Das einzige, was ich dieses Verhalten zu rechtfertigen, um zu versuchen zu finden ist dieser Satz in Abschnitt 9.4.2, Absatz 1:

A static data member is not part of the subobjects of a class.

Allerdings sind die beiden vorgenannten Absätzen noch gültig ist. Außerdem habe ich das gleiche Beispiel versucht, das auf andere statische Mitglieder wie eine Variable, eine Funktion und eine Funktionsschablone verweist, und alle von ihnen kompilieren erfolgreich in GCC und Clang.

class example{ 
    public: 
     static int constexpr variable = 1; 
     void static function(){ return; } 
     template <class T> void static function_template(){ return; } 
}; 

int main(){ 

    example obj; 

    int a = obj.variable; 
    int b = example::variable; 

    obj.function(); 
    example::function(); 

    obj.function_template<int>(); 
    example::function_template<int>(); 

    return 0; 
} 

Vielen Dank im Voraus.

Hinweis 1: Compilerversionen sind 3.7.0 und gcc 5.2.1.

Anmerkung 2: das Schlüsselwort static erforderlich: Variable template at class scope

Anmerkung 3: da ich die Variable Vorlage initialisieren möchten, das Schlüsselwort constexpr auch, weil in meinem eigentlichen Code erforderlich ist, werde ich es mit float, double instanziiert und lang doppelt (siehe C++ 14 Standard (ISO/IEC 14882: 2014), Abschnitt 9.4.2, Absatz 3).

Hinweis 4: tatsächliche "Definitionen" dieser statischen Datenelemente außerhalb der Klasse (d. H. template <class T> constexpr T example::var;) werden in diesem Beispiel nicht benötigt. Ich habe es auch versucht, aber es macht keinen Unterschied.

+1

Welche Version von gcc verwenden Sie? g ++ 6.2 verhält sich wie clang. Aber im Gegensatz zu clang lautet die Fehlermeldung für 'obj.var ', dass * "' var' ist keine Elementvorlagenfunktion "*. Es scheint, dass der erste Teil seit g ++ 5 behoben wurde. – Holt

+0

Ich benutze gcc 5.2.1 und clang 3.7.0 :) –

+1

Clang Stamm akzeptiert diesen Code. –

Antwort

2

Ich habe Ihren ersten Code in Visual Studio 2015 kopiert (es wurde erfolgreich kompiliert). Ich fügte einige Ausgänge über std::cout hinzu, wo ich fand b gibt einen Compiler-Fehler: uninitialized local variable 'b' used. a auf der anderen Seite wurde erfolgreich gedruckt, wenn b nicht verwendet wurde. Es scheint also, dass C++ beim Zugriff auf statische Template-Member, wie Sie es gesagt haben, ein wenig heikel ist, so dass Sie es mit seinem vollständigen, qualifizierten Namen referenzieren müssen.

Vielleicht noch neugierig sind die folgenden Zeilen:

std::cout << example::var<int> << "a\n"; 

Die obige Zeile wie erwartet funktioniert, 1.5 zu 1 abgeschnitten ausgibt und eine 'a' mit einer neuen Zeile. Nichts, worüber man nach Hause schreiben könnte.

std::cout << obj.var<int> << "b\n"; 

Jetzt ist hier, wo es interessant wird ... nicht nur die obige Zeile keinen Wert für obj.var<int> ausdrucken, die 'b'\n auch nie gedruckt wird. Ich habe sogar gegen die std::coutgood()fail() und bad() Funktionen getestet, von denen keine berichtet, dass etwas falsch war (und weitere Verwendungen von std::cout wurden erfolgreich ausgegeben ausgeführt).

Eine weitere Kuriosität, die ich fand, war, dass auto x = obj.var legal ist, und zu finden, x ist vom Typ example. Nun, dies in einem Compiler-Fehler unterschiedliche Ergebnisse mit einem globalen Vorlage zu tun (wie erwartet ich die erste Person als auch):

template<typename T> constexpr T ex = 1.5; 
auto x = ex // compiler error: argument list for variable template "ex" is missing 

Außerdem fand ich, dass der Zugang var durch eine andere Vorlage statische Funktion erfolgreich war, weiter impliziert dass Elementauswahl funktioniert einfach nicht in diesem Fall

class example 
{ 
public: 
    template <class T> static constexpr T var = T(1.5); 
    template <typename T> static void thing() 
    { 
     std::cout << var<T> << '\n';   // works 
     std::cout << example::var<T> << '\n'; // also works 
    } 
}; 

nun so weit wie der Standard geht, ich bin geneigt, ihre Phrasierung ist nur ein bisschen zu glauben ... pedantisch. Die Abschnitte, die Sie haben von der Norm zitiert:

it is not necessary to use the class member access syntax (5.2.5) to refer to a static member.

und

A variable template at class scope is a static data member template.

würde zu implizieren scheint, dass dies funktionieren würde. Ich glaube, der Punkt, an dem diese Zitate in diesem Fall nicht gelten, ist die Tatsache, dass eine Vorlage (von irgendetwas) nicht wirklich existiert, bis sie in einer Übersetzungseinheit instanziiert wird (dh warum der Code für Vorlagen oft darin enthalten ist) die Header-Datei selbst).

Aus diesem Grund, obwohl die Template-Variable ein Mitglied einer Klasse sein kann, sind seine Instanziierungen nicht ... aus irgendeinem Grund ... und erfordern daher den Scope-Resolution-Operator im Gegensatz zum Member Selection-Operator.

Aber IMO, den Zugriff auf statische Daten über den Elementauswahloperator ist eine schlechte Praxis, da statische Daten und Funktionen nicht tatsächlich Teil eines bestimmten Objekts sind. Der Zugriff auf statische Daten auf die gleiche Weise wie bei nicht statischen Daten kann dazu führen, dass relativ unschuldig wirkender Code tatsächlich fehlerhafte Logik ist. Zum Beispiel, wenn Sie aus irgendeinem Grund eine nicht-const statische Element namens something hätten, könnten Sie schreiben example_object.something = 42, nicht erwarten, dass etwas für alle anderen Instanziierungen dieser Klasse in Ihrem Programm (die gleichen Probleme wie globale Variablen, wirklich) zu ändern. Aus diesem Grund (und der Tatsache, dass die Zugriffssyntax für Mitglieder sowieso nicht für statische Variablen des Templates funktioniert), empfehle ich immer, die Auflösung des Bereichs zu verwenden, um auf statischen Inhalt von außerhalb der Klasse zuzugreifen/ihn zu modifizieren. example_class::something = 42 ist viel klarer, dass wir something für alle Instanzen von example_class ändern. In der Tat erfordern einige modernere Sprachen wie C# Sie auf statische Daten über den Klassennamen zugreifen, es sei denn, Sie sind innerhalb der Klasse.

Angesichts der Tatsache, dass mehrere Compiler Fehler für verschiedene Teile dieses kleinen Beispielprogramms sind, würde ich wetten, dass es im Standard nicht sehr gut abgedeckt ist (und wahrscheinlich nicht oft in der Praxis verwendet), und die Compiler nur behandeln es anders (ein weiterer Grund, es zu vermeiden).

tl; dr

Anscheinend während Elementauswahl Syntax für statische Member-Variablen arbeitet, ist es nicht für die statische Membervariablen Vorlage arbeiten (obwohl der Compiler nicht zu beklagen scheint). Die Scope-Resolution-Syntax funktioniert jedoch, und IMO sollte sowieso bevorzugt werden.

+0

Achten Sie darauf, Ihre Streams mit 'std :: flush' oder' std :: endl' zu löschen. Das könnte erklären, warum Sie nichts mit 'std :: cout << obj.var <<" b \ n ";'. –

+0

@ FrançoisAndrieux Nun, ich habe gesagt, dass ich auch den Stream danach verwendet habe (was erfolgreich war, aber ich habe vergessen zu erwähnen, dass es auch angezeigt wurde), also sollte alles in dem Puffer zusammen mit diesem Zeug angezeigt werden – DeMayo

+0

Dank @DeMayo um sich die Zeit zu nehmen, dieses Problem zu überprüfen. Ihre Analyse ist interessant und bestätigt, was ich dachte (und Sie auch sagen): "neue" C++ 14 Variable Templates sind weder vom Standard noch von den Compilern sehr gut abgedeckt. Tatsächlich gibt es im Standard-Komitee noch einige Active Core Language-Probleme bezüglich variabler Vorlagen: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html Ich stimme auch mit Ihren Beratung über den Zugriff auf statische Daten mit der Scope-Auflösung, ich habe nur den Standard und die Compiler getestet. –

Verwandte Themen