2013-04-06 16 views
20

Ho questo codiceIl compilatore pensa che "A (A &)" accetta rvalues ​​per un momento?

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

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

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

Con mia grande sorpresa questo non funziona con GCC e Clang. Clang dice per esempio

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

perché scelgono il primo f, quando la seconda f funziona bene? Se rimuovo il primo f, la chiamata ha esito positivo. Che cosa è più strano per me, se io uso l'inizializzazione brace, funziona anche bene

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

Tutti chiamano il secondo f.

risposta

17

È una stranezza linguistica. Il primo f corrisponde meglio perché il tuo A non richiede alcuna conversione per corrispondere al tipo di argomento (A), ma quando il compilatore tenta di effettuare la chiamata il fatto che non sia stato trovato alcun costruttore di copia adatto causa il fallimento della chiamata. La lingua non consente di tenere conto della fattibilità della chiamata effettiva quando si esegue la fase di risoluzione del sovraccarico.

più vicina citazione standard ISO corrispondenza/IEC 14882: 2011 13.3.3.1.2 sequenze di conversione definiti dall'utente [over.ics.user]:

Una conversione di un'espressione di tipo di classe alla stessa classe il tipo è assegnato al rango di corrispondenza esatta e una conversione di un'espressione di tipo classe a una classe base di quel tipo viene indicata come rango di conversione, nonostante il fatto che un costruttore copia/sposta (ovvero una funzione di conversione definita dall'utente)) è chiamato per quei casi.

Per il caso elenco di inizializzazione, probabilmente avete bisogno di guardare: 13.3.3.1.2 sequenze di conversione definiti dall'utente [over.ics.user]

Quando gli oggetti di classe non-aggregata tipo T sono elenco-inizializzato (8.5.4), la risoluzione di sovraccarico seleziona il costruttore in due fasi:

- Inizialmente, le funzioni candidate sono i costruttori inizializzatore-list (8.5.4) della classe T e la la lista degli argomenti è costituita dall'elenco di inizializzazione come cantare argomento.

- Se non viene trovato nessun costruttore di inizializzazione-list praticabile, risoluzione di sovraccarico viene eseguita di nuovo, in cui le funzioni candidati sono tutti i costruttori della classe T e la lista degli argomenti è costituito da elementi della lista di inizializzazione.

Perché la risoluzione di sovraccarico deve esaminare Case costruttrici vitali in ogni caso per f(A) e f(B) deve respingere il seqence cercando di legare A() a A(A&) ma B(const A&) è ancora vitale.

+0

Grazie! Non riesco a trovare nessuna regola per il caso '{...}'. Questo spiega perché il caso '{...}' funziona? –

+0

@ JohannesSchaub-litb: Non sono sicuro, tbh, stai chiamando la funzione con un _braced-init-list_ così le regole sono decisamente diverse. –

+0

@ JohannesSchaub-litb Vedere [over.ics.list]. Penso che abbia a che fare con [over.ics.ref]/3 (ho interpretato male il tuo codice in precedenza): quando si forma il sottoinsieme di funzioni vitali, il ctor 'A (A &)' non è considerato vitale in quanto lega un riferimento temporaneo a un valore non costante. – dyp

Problemi correlati