2017-12-19 4 views
4

Der folgende Code einen nicht definierten Referenzverknüpfungsfehler gibt:statische constexpr Vorlagen-Element gibt undefined-Referenz, wenn spezialisierte

template<int> 
struct X { 
    static constexpr int x = 0; 
}; 

template<> 
constexpr int X<1>::x; 

int main() 
{ 
    return X<1>::x; 
} 

Aber ich weiß nicht genau, warum.

Ist es möglich, ein Datenelement zu definieren, ohne die gesamte Vorlage zu spezialisieren?

Um klar zu sein: Dieser Code kompiliert fein, aber gibt einen Linker-Fehler (undefined-Referenz).

+1

Vielleicht sollte Ihre "Und" Frage die führende sein. – DeiDei

+0

Ja, bearbeitet ;-) – wimalopaan

Antwort

3

Is it possible to define a data-member without [specializing] the whole template?

static Daten Mitglieder einer Klasse Vorlage sind erlaubt explizit spezialisiert werden ([temp.expl.spec]), aber wenn Sie dies tun möchten, können Sie nicht bereits können einen Initialisierer für das Element festlegen, innerhalb die Klassenvorlage (class.static.data). Das heißt,

wenn wir ersetzen constexpr mit const, würde dieser Code in Ordnung sein:

template<int> 
struct X { 
    static const int x; 
}; 

template<int I> 
const int X<I>::x = 0; 

template<> 
const int X<1>::x = 1; 

Aber dieser Code würde NICHT in Ordnung sein:

template<int> 
struct X { 
    static const int x = 0; 
}; 

template<> 
const int X<1>::x = 1; 

Sie können sehen, der Unterschied ist, Wo initialisieren wir die Variable für die primäre Vorlage.

Wenn wir nun ersetzen const mit constexpr wollen, dann sind wir benötigt einen Initialisierer (class.static.data) zur Verfügung zu stellen:

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

So wir in dieser seltsamen Situation enden, wo Wir können das static Mitglied spezialisieren, aber nicht, wenn es constexpr ist, weil constexpr einen Initialisierer erfordert. IMHO ist das ein Manko des Standards.

Es scheint jedoch nicht, dass alle modernen Compiler zustimmen.

gcc 8.0.0 kompiliert (aber verlinkt) Ihren Code nicht, wie er ist (falsch), wenn Sie jedoch einen Initialisierer für die Spezialisierung hinzufügen, beschwert er sich über doppelte Initialisierung (rechts).

Klirren 6.0.0 lässt sich nicht kompilieren Sie den Code wie besehen (rechts), aber wenn man die initializer hinzufügen funktioniert es ohne Probleme (falsch, aber das ist wahrscheinlich das, was die Standard diktieren sollte)

MSVC 19.00.23506 kompiliert den Code nicht so wie er ist (rechts), UND er kompiliert den Code nicht, wenn Sie den Initialisierer hinzufügen (sich über die Neudefinition beschweren) (rechts).

Am Ende könnte es nur einfacher, die Spezialisierung in eine Helfer Traits Klasse zu drücken:

template<int> 
struct X_Traits{ 
    static constexpr int value = 0; 
}; 

template<> 
struct X_Traits<1>{ 
    static constexpr int value = 1; 
}; 

template<int I> 
struct X { 
    static constexpr int x=X_Traits<I>::value; 
    // ... 
}; 

In C++ 17 und darüber hinaus nutzen wir constexpr if machen zu vermeiden, benötigen Spezialisieren Sie unsere Traits Klasse:

template<int I> 
struct X_Traits{ 
    static constexpr int get_value(){ 
     if constexpr(I==1){ 
      return 1; 
     }else{ 
      return 0; 
     } 
    } 
}; 

template<int I> 
struct X { 
    static constexpr int x=X_Traits<I>::get_value(); 
    // ... 
}; 

int main(){ 
    static_assert(X<0>::x == 0); 
    static_assert(X<1>::x == 1); 
} 
+0

Aber das erklärt nicht, warum der obige Code gut kompiliert, aber einen Linker-Fehler gibt. – wimalopaan

+1

@wimalopaan: Der obige Code scheint in gcc (ein Bug) zu kompilieren, aber nicht mit clang oder msvc. Was ich versuchte zu sagen, war, dass der Code schlecht geformt war, weil er versucht, ein existierendes "statisches consxpr" -Mitglied neu zu definieren ("kann kein einzelnes Mitglied einer Vorlagenklasse spezialisieren"). – AndyG

+0

Aber der Auszug, den StoryTeller uns von der C gibt ++ Standard erwähnt ausdrücklich die Spezialisierung. Also muss eine Spezialisierung eines einzelnen Mitglieds möglich sein? Oder lese ich das obige Zitat des Standards falsch? – wimalopaan

2

so.

template<int i> 
struct X { 
    static constexpr int x = i==0?2:10; 
}; 

int main() 
{ 
    return X<1>::x; 
} 

Beachten Sie, dass Sie es außerhalb der Klasse nicht definieren müssen.

+0

Ja das weiß ich. Aber ich möchte die Ursache des obigen Fehlers wissen. – wimalopaan

+0

Große Verwendung eines konstanten Ausdrucks, um das Mitglied zu initialisieren! Es könnte ein wenig verschachtelt werden, wenn man sich für mehr als einen Wert spezialisieren möchte, aber ich mag die Einfachheit davon. – AndyG

4

Sie stolperte über eine "kleine" Frage wegen der Spezialisierung. Wenn wir [temp.expl.spec]/13 verweisen:

An explicit specialization of a static data member of a template or an explicit specialization of a static data member template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [ Note: The definition of a static data member of a template that requires default-initialization must use a braced-init-list:

template<> X Q<int>::x;       // declaration 
template<> X Q<int>::x();      // error: declares a function 
template<> X Q<int>::x { };      // definition 

 — end note ]

Was bedeutet, dass Sie erklärenX<1>::x zu existieren, aber es nicht definieren. Daher ist es undefiniert.

Was ich verrückt finde, ist, dass dein Compiler es einfach akzeptiert. Sie können constexpr Variablen nicht deklarieren, ohne sie im Allgemeinen zu definieren. Das ist ziemlich merkwürdig.

+0

Was ist seltsamer ist, wenn Sie einen Wert für die "Spezialisierung" liefern Clagon magisch akzeptiert, während gcc und MSVC nicht. Ich hätte gedacht, dass dies von Anfang an eine Neudefinition war, aber jetzt bin ich mir nicht sicher. – AndyG

+1

@AndyG - Das ist enttäuschend. Das sind in der Tat zwei gegensätzliche Kräfte (die Spezialisierung ist eine Deklaration, und der "Constexpr" erfordert einen Initialisierer), aber es sollte nicht so schwer zu versöhnen sein. – StoryTeller

+0

Sieht aus wie ein Sprachfehler? – wimalopaan

Verwandte Themen