2016-04-05 5 views
1

Bitte geben Sie die folgende Typdefinition berücksichtigen:nicht-ODR-Typ verwendet literal Erstellen

struct lit 
{ 
    enum { A, B } value; 
    constexpr lit() : value(A) { } 
    constexpr lit(int) : value(B) { } 
}; 

Gemäß dem Standard 14 C++ (3,9/10) qualifiziert dieser Art als Literal-Typ. Als nächstes betrachten Sie bitte folgende Nutzungsszenario:

struct foo 
{ 
    static constexpr lit a { }; 
    static constexpr lit b { 10 }; 
    static constexpr int i { 42 }; 
}; 

int main() 
{ 
    lit a = foo::a; 
    lit b = foo::b; 
    int i = foo::i; 

    std::cout << "a=" << a.value << std::endl; 
    std::cout << "b=" << b.value << std::endl; 
    std::cout << "i=" << i  << std::endl; 
    return 0; 
} 

Abschnitt 9.4.2/3 der Norm, die zwar nicht direkt die besagt, bedeutet, dass ich muss sie nicht angeben Definition für static constexpr Mitglieder, es sei denn, sind odr-used (zB in Betracht ziehen foo::i).

Wenn ich das oben mit clang kompiliere, beschwert es sich, dass ich Definitionen für foo::a und foo::b vermisse, da es denkt, dass sie odr-used sind. Siehe Beispiel unter coliru.

gcc dagegen schluckt es einfach gut und druckt glücklich das richtige Ergebnis aus. Auch hier ist ein Beispiel auf coliru.

Also, meine Frage ist zweifach:

  1. Sind foo::a und foo::b wirklich odr-Einsatz? Und wenn ja, bitte erläutern Sie, warum und wie sie sich von foo::i unterscheiden.

  2. Welche Änderungen lit getan werden müssen, um sie für foo::a und foo::b out-of-Klassendefinitionen nicht benötigen? Mit anderen Worten, ich möchte foo::a und foo::b so verhalten wie foo::i.

UPDATE:

Es scheint, Barry Antwort in Bezug auf lit richtig sein odr verwendet werden. Zur Veranschaulichung, wenn ich die folgende Funktion hinzu:

void bar(const int& x) 
{ 
    std::cout << "bar(" << x << ")" << std::endl; 
} 

und die folgende Zeile zum Haupt:

bar(foo::i); 

gcc jetzt klagt auch.

Antwort

3

Sind foo::a und foo::b wirklich odr-used?

Ich glaube, die Antwort ist ja. Wir sind Kopie Konstruktion a und b. lit hat eine implizite Copykonstruktor die lautet:

constexpr lit(lit const& rhs) = default; 

Das heißt, wir foo::a und foo::b auf eine Referenz Bindung, die sie macht odr verwendet werden. Dies ist kein Problem für foo::i, da int kein Klassentyp ist und daher keinen Kopierkonstruktor hat.

Ich bin nicht sicher, was Sie tun können, um dies zu vermeiden, außerhalb nur die beiden Literale nicht kopieren und stattdessen die enums (d. H.auto a = foo::a.value; verwendet nicht foo::a).

+1

Aber ich denke, das fällt unter eine Ausnahme in [basic.defodr]/3, weil 'lit' einen * trivialen * Kopierkonstruktor hat, der verwendet wird, um die lvalue-to-rvalue-Konvertierung durchzuführen. – Brian

+0

@Barry, danke für deine Antwort, aber was ist mit 3.2/3 ... oh yeah, das ist, was Brian über –

+0

sagt @Brian Es ist ein sehr ungeschickt formulierter Satz. Es besagt, dass es die Lvalue-to-rvalue-Konvertierung ist, die keine nicht-trivialen Funktionen aufruft? Aber egal, wo konvertieren wir in rvalue? – Barry