2016-12-10 30 views
10

Pytanie powstało w kontekście this answer.Dlaczego inicjowanie listy bezpośredniej powoduje niejednoznaczność przy rzutowaniu typu referencyjnego, jeśli deklarowane są operatory rzutowania na typ i odniesienie do typu?

Rozważmy przykład:

struct foo { 
    int value; 
    operator int&(){ return value; } 
    operator int(){ return value; } 
}; 

int main() { 
    int &a(foo{}); // #1 
    //int &b{foo{}}; // #2 -- ambiguity 
    int &c = foo{}; // #3 
    //int &d = {foo{}}; // #4-- ambiguity 
    int &d { a }; // #5 
    int &e = { a }; // #6 
    (void)a; 
    (void)c; 
    (void)d; 
    (void)e; 
} 

Nie rozumiem dlaczego # 2 i # 4 przyczyna niejednoznaczności podczas # 1 i # 3 nie. Tak więc pytanie brzmi: dlaczego inicjowanie listy bezpośredniej powoduje niejednoznaczność dla niejawnego rzutowania na odniesienie, jeśli deklarowane są operatory rzutowania na typ i odniesienie do typu?

+2

'int & a (foo {})' to bezpośrednia inicjalizacja. 'int & b {foo {}}' to bezpośrednia inicjalizacja * listy *. To nie to samo. –

Odpowiedz

7

Inicjowanie listy, gdy jest używana do zainicjowania odniesienia, przyjmie wymienione wartości i przekształci je w wartość prynu (aka: a temporary), która będzie używana do zainicjowania referencji.

Tak int &b{foo{}} jest funkcjonalnie równoważne int &b(int(foo{})). Co jest niejednoznaczne; może wygenerować, że int przez lub operator int&.

Ale nawet gdyby nie było niejednoznaczne, by nadal być coraz non-const odniesienia lwartości do prvalue. Co jest nielegalne. Tak więc kod ten był nigdy nie pracował nad.

usztywnione-init-list (nawiasy klamrowe) zainicjować obiektów, nie referencje do obiektów. Jeśli masz już obiekt i chcesz się do niego odwoływać, nie używaj list wzmacnianych-init.


Ale w tym przypadku dlaczego kompilator zaakceptować # 5?

Ponieważ inicjalizacja listy jest serią reguł o priorytecie. Reguła, która ma wyższy priorytet niż ten, który wskazałem powyżej, jest przypadkiem listy wzmocnionej-init, która zawiera pojedynczą wartość, której typ jest identyczny z typem inicjowanego. # 5 i 6 właśnie tak się stało, że pasują do tego rachunku, ponieważ d, e i a są wszystkie int& s.

Ale jeśli tylko weźmiesz moją radę i nie użyjesz spreparowanych list, kiedy nie będziesz próbował stworzyć obiektu, nie będziesz musiał się martwić takimi przypadkami.

+3

Jak nie wszyscy wiedzą o prvalue (na przykład ja). Oto link do jego wyjaśnienia: http://stackoverflow.com/a/3601661/783510 –

+0

Ale w tym przypadku dlaczego kompilator akceptuje # 5? czy nie jest to podobne do 'int & d (int (a));'? –

+1

@ W.F .: Zobacz moje zmiany dla twojej odpowiedzi. –