2013-04-06 13 views
20

Mam ten kodKompilator myśli, że "A (A &)" akceptuje rvalues ​​na chwilę?

struct A { A(); A(A&); }; 
struct B { B(const A&); }; 

void f(A); 
void f(B); 

int main() { 
    f(A()); 
} 

ku mojemu zdziwieniu to zawiedzie z GCC i Clang. Dzyń mówi na przykład

Compilation finished with errors: 
source.cpp:8:10: error: no matching constructor for initialization of 'A' 
     f(A()); 
     ^~~ 
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument 
    struct A { A(); A(A&); }; 
        ^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided 
    struct A { A(); A(A&); }; 
      ^
source.cpp:4:13: note: passing argument to parameter here 
    void f(A); 

Dlaczego oni wybrać pierwszą f, gdy drugi f działa poprawnie? Jeśli usuniemy pierwszy kod f, to połączenie powiedzie się. Co jest bardziej dziwne dla mnie, jeśli mogę użyć inicjalizacji nawiasów, również działa dobrze

int main() { 
    f({A()}); 
} 

Wszyscy nazywają drugą f.

Odpowiedz

17

To dziwactwo językowe. Pierwszy numer f jest lepszy, ponieważ twój A nie wymaga żadnej konwersji, aby dopasować typ argumentu (A), ale gdy kompilator podejmie próbę nawiązania połączenia, fakt, że nie można znaleźć odpowiedniego konstruktora kopiowania, powoduje niepowodzenie wywołania. Język nie pozwala uwzględnić wykonalności faktycznego wezwania pod uwagę przy wykonywaniu kroku zmniejszania rozdzielczości.

najlepiej dopasowanych średnia cytat ISO/IEC 14882: 2011 13.3.3.1.2 zdefiniowanych przez użytkownika sekwencji konwersji [over.ics.user]:

Konwersja wyrażenia typu klasy do tej samej klasy typ jest podany Dokładny ranking dopasowania, a konwersja wyrażenia typu klasy na klasę podstawową tego typu ma nadany stopień konwersji, pomimo faktu, że konstruktor kopiowania/przenoszenia (tj. zdefiniowana przez użytkownika funkcja konwersji) jest powołany w tych przypadkach.

Na razie lista inicjalizacji, prawdopodobnie trzeba spojrzeć na: 13.3.3.1.2 definiowane przez użytkownika sekwencji konwersji [over.ics.user]

Jeżeli obiekty nie-klasy zbiorczej T typu są lista inicjalizacji (8.5.4), rozdzielczość przeciążenie wybiera konstruktora w dwóch etapach:

- początkowo funkcje kandydujące konstruktorzy inicjatora-list (8.5.4) klasy T i lista argumentów składa się z listy inicjalizatorów jako śpiew le argument.

- W przypadku braku realnej initializer-lista konstruktor zostanie znaleziony, rozdzielczość przeciążenie jest wykonywana ponownie, gdzie funkcje kandydujące są wszyscy konstruktorzy klasy T i lista jej argumentów składa się z elementów listy inicjatora.

Ponieważ rozdzielczość przeciążenie musi spojrzeć na żywych contructors każdorazowo dla f(A) i f(B) musi odrzucić seqence próbując związać A() do A(A&) ale B(const A&) jest wciąż opłacalne.

+0

Dzięki! Nie mogę znaleźć takiej reguły w przypadku "{...}". Czy to wyjaśnia, dlaczego przypadek '{...}' działa? –

+0

@ JohannesSchaub-litb: Nie jestem pewien, tbh, wywołujesz funkcję z _braced-init-list_, więc zasady są zdecydowanie inne. –

+0

@ JohannesSchaub-litb Zobacz [over.ics.list]. Myślę, że ma to związek z [over.ics.ref]/3 (błędnie odczytałem twój kod wcześniej): Podczas tworzenia podzbioru funkcjonalnych funkcji, ctor 'A (A &)' nie jest uważany za wykonalny, ponieważ wiąże tymczasowe z referencją o stałej wartości l. – dyp

Powiązane problemy