2014-10-29 10 views
11

Wie unterscheidet sich die Initialisierung von {} in einer Konstruktorinitialisierungsliste von der Initialisierung von() beim Initialisieren des Verweises auf abstrakte Typen? Nehmen Klasse Bar unten:Warum funktioniert C++ 11 geschweifte Klammerinitialisierung in der Konstruktorinitialisierungsliste nicht, wenn Parens initialisiert wird?

class AbstractBase 
{ 
public: 
    AbstractBase() {} 
    virtual ~AbstractBase() = default; 

    virtual void ab() = 0; 
}; 

class Foo : public AbstractBase 
{ 
public: 
    Foo() {} 

    void ab() {} 
}; 

class Bar 
{ 
public: 
    Bar(const AbstractBase& base) : myBase{base} {} 

private: 
    const AbstractBase& myBase; 
}; 


int main() 
{ 
    Foo f{}; 
    Bar b{f}; 

} 

Beim Kompilieren, erhalte ich die Fehler

test5.cpp: In constructor ‘Bar::Bar(const AbstractBase&)’: 
test5.cpp:22:48: error: cannot allocate an object of abstract type ‘AbstractBase’ 
    Bar(const AbstractBase& base) : myBase{base} 
               ^
test5.cpp:2:7: note: because the following virtual functions are pure within ‘AbstractBase’: 
class AbstractBase 
    ^
test5.cpp:8:18: note: virtual void AbstractBase::ab() 
    virtual void ab() = 0; 

Ändern der Linie

Bar(const AbstractBase& base) : myBase(base) {} 

es kompiliert und läuft gut.

Beim Lesen von Stroustrups C++ 11-Buch hatte ich den Eindruck, dass {} in fast allen Fällen dasselbe war(), mit Ausnahme von Zweideutigkeiten zwischen Konstruktoren, die std :: initializer_list <> und andere annehmen Konstruktoren und Fälle, in denen Auto als Typ verwendet wird, von denen ich hier keine mache.

+1

Meine Faustregel: Verwenden Sie '{}' für Listen von Elementen (einschließlich Null) und '()', um explizit einen anderen Konstruktor aufzurufen. –

+1

Dies ist eigentlich ein Problem mit der Initialisierung der Liste von Referenzen zu tun –

+0

Das gleiche Problem wie hier - http://StackOverflow.com/Questions/19347004/Copy-Constructor-Curly-Braces-Initialization –

Antwort

12

Kurze Antwort: Dies war ein Fehler im Standard, der in C++ 14 behoben ist, und g ++ 4.9 hat den Fix (rückwirkend auch auf den C++ 11-Modus angewendet). Defect Report 1288


Hier ist ein einfacheres Beispiel:

struct S 
{ 
    int x; 
    S() { }  // this causes S to not be an aggregate (otherwise aggregate 
       // initialization is used instead of list initialization) 
}; 

S x = 5; 
S const &y { x } ;  

x = 6; 
std::cout << y << std::endl;  // output : 5 

Im Text von C++ 11, die Bedeutung von S const &y {x}; ist nicht y-x zu binden; in der Tat besteht die Bedeutung darin, ein Temporäres zu erzeugen und einen Bezug darauf zu binden. Von ++ 11 C [dcl.init.ref]/3:

Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note ]

Diese recht albern ist, eindeutig die Absicht dieses Codes ist y direkt an x zu binden. In C++ 14 wurde der Text geändert:

Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element;

Da ein Typ ist auf sich selbst Bezug bezogene (oder einer ihrer Basisklassen), hier in meiner Probe und in Ihrem eigentlichen Code, sollte es eigentlich richtig binden .


Ihre Fehlermeldung kommt vom Compiler nach der C++ 11 Wortlaut und eine temporäre aus base zu schaffen versucht, den Verweis zu binden; und dies schlägt fehl, da base von einem abstrakten Typ ist.

+0

Wenn ich const aus meiner Referenz lösche, wenn ich {} Initialisierung verwende, erhalte ich einen anderen Fehler: 'test5.cpp: Im Konstruktor 'Bar :: Bar (AbstractBase &)': test5 .cpp: 22: 42: Fehler: ungültige Initialisierung einer nichtkonstanten Referenz vom Typ 'AbstractBase &' aus einem rvalue vom Typ '' Balken (AbstractBase & base): myBase {base} {} ^ ' –

+3

@oryan_dunn das versucht, eine temporäre an eine nicht-const Referenz –

+0

zu binden, ja, ich weiß, dass es jetzt versucht, eine temporäre von einem abstrakten Typ zu machen. Ich denke, bis C++ 14 kommt, muss ich weiterhin() auf alle meine Konstruktor-Initialisierungslisten konsistent zu bleiben. –