2014-10-29 19 views
11

W jaki sposób inicjalizacja {} na liście inicjalizacji konstruktora różni się od inicjalizacji() podczas inicjowania odwołania do typów abstrakcyjnych? Take poniżej class Bar:Dlaczego inicjalizacja C++ 11 inicjalizująca nawias klamrowy na liście inicjalizacyjnej konstruktora nie działa podczas inicjowania parens?

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

} 

Podczas kompilacji, pojawia się błąd

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; 

Zmiana linii

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

kompiluje i działa poprawnie.

Czytając książkę C++ 11 w Stroustrup, miałem wrażenie, że {} było takie samo jak() w większości przypadków, z wyjątkiem sytuacji, gdy istniała niejednoznaczność między konstruktorami, którzy pobierają std :: initializer_list <> i inne konstruktorzy i przypadki, w których używa się auto jako typu, z których żadne tutaj nie robię.

+1

Moja zasada: Użyj '{} dla list elementów (w tym zera), a'() 'do jawnego wywołania dowolnego innego konstruktora. –

+1

To jest rzeczywiście problem związany z inicjowaniem listy referencji –

+0

Ten sam problem co tutaj - http://stackoverflow.com/questions/19347004/copy-constructor-curly-braces-initialization –

Odpowiedz

12

Krótka odpowiedź: To był błąd w standard, który jest zamocowany w C++ 14, oraz g ++ 4.9 zawiera poprawki (wstecznie zastosowany tryb C++ 11 też). Defect Report 1288


tutaj prostszy przykład:

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 

W tekście C++ 11 znaczenie S const &y {x}; nie wiążą y do x IS; w rzeczywistości oznacza to stworzenie tymczasowego i wiążącego odniesienie do tego. Spośród takich jak C++ 11 [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 ]

to dość głupie wyraźnie intencją tego kodu jest wiązanie y bezpośrednio x. W C++ 14 tekst został zmieniony:

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;

Ponieważ typu jest do siebie (lub jeden z jej klas bazowych) związane odniesienia, w mojej próbce tutaj oraz w rzeczywistym kodzie, to rzeczywiście powinien wiązać poprawnie .


Twój komunikat o błędzie pochodzi z kompilatora C++ następującym po 11 brzmienia i próbuje utworzyć tymczasowy z base związać odniesienie do; i to się nie powiedzie, ponieważ base jest typu abstrakcyjnego.

+0

Jeśli usuwam const z mojego odwołania podczas używania {} initialization, pojawia się inny błąd: 'test5.cpp: In constructor 'Bar :: Bar (AbstractBase &)': test5 .cpp: 22: 42: error: niepoprawna inicjalizacja nie-const referencji typu "AbstractBase &" od wartości rinalnej typu " ' Bar (AbstractBase i baza): myBase {base} {} ^' –

+3

@oryan_dunn, który próbuje powiązać tymczasowy z niestanowiącą wartością stałą –

+0

tak, zdaję sobie sprawę, że teraz, próbuję zrobić tymczasowy typ abstrakcyjny. Sądzę, że do czasu pojawienia się C++ 14, będę musiał nadal używać() na wszystkich listach inicjujących konstruktora, aby zachować spójność. –

Powiązane problemy