2015-10-10 4 views
7
struct A { A(int);}; 
struct B { explicit B(A); B(const B&);}; 
B b({0}); 

gcc 5.1.0 dà l'errorerisoluzione di sovraccarico ottiene risultato diverso tra il GCC e clang

/dev/fd/63:3:8: error: call of overloaded 'B(<brace-enclosed initializer list>)' 
is ambiguous 
/dev/fd/63:3:8: note: candidates are: 
/dev/fd/63:2:27: note: B::B(const B&) 
/dev/fd/63:2:21: note: B::B(A) 

mentre 3.6.0 clang riesce.

Quale è giusto? Perché?

Per gcc 5.1.0: http://melpon.org/wandbox/permlink/pVe9eyXgu26NEX6X

Per clang 3.6.0: http://melpon.org/wandbox/permlink/WOi1md2dc519SPW0

Questo può essere simile a Direct list initialization compiles successfully, but normal direct initialization fails, why? cui gcc e clang ottenere lo stesso risultato.

Ma questa è una domanda diversa. B(A) è esplicito qui. gcc e clang ottengono risultati diversi.

+0

Duplicato di http://stackoverflow.com/q/32469979/3647361? – Columbo

+0

@Columbo 'B (A)' è esplicito qui e http://stackoverflow.com/questions/32469979/direct-list-initialization-compiles-successfully-but-normal-direct-initializati sia gcc che clang ottengono lo stesso risultato ma qui è diverso. – stackcpp

+0

La mia ipotesi è che è dovuto a diversi standard di default/supporto standard. –

risposta

3

La differenza può essere ridotto a

struct A { explicit A(int); }; 
struct B { B(int); }; 
void f(A); 
void f(B); 

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

Su GCC questo non riesce, in conformità alla norma (che dice che per la lista di inizializzazione, costruttori espliciti sono considerate - in modo che possano produrre un'ambiguità - ma hanno semplicemente non sono autorizzati a essere selezionati). Clang accetta e chiama la seconda funzione.

Nel vostro caso, ciò che @Columbo dice nella sua risposta allo Direct list initialization compiles successfully, but normal direct initialization fails, why? si applica. Con la differenza che nel tuo caso, B(const B&); non è più accettabile per Clang perché la conversione {0} -> B avrebbe dovuto affrontare due possibilità: il costruttore esplicito o l'utilizzo ricorsivo del costruttore di copie una seconda volta. La prima opzione, come spiegato sopra, non verrà considerata da clang e questa volta si applica la spiegazione di @Columbo e il costruttore di copia non può essere utilizzato una seconda volta perché ciò richiederebbe una conversione definita dall'utente poiché abbiamo un singolo elemento (qui, 0). Quindi nel sommario, solo il primo costruttore ha successo e viene preso.


Da quando ho capito il problema è sulle regole strane risoluzione di sovraccarico e alcuni potrebbe non essere in grado di seguire, ecco una spiegazione più intuitiva. Le regole che sono attivi sono, in ordine

  • b({0}) mezzi di avanzamento http://eel.is/c++draft/dcl.init#17 e da lì a http://eel.is/c++draft/over.match.ctor che è la prima OR contesto. I due costruttori elencati sono B(A); e B(const B&) con argomento {0}.

    • Per B(A) funziona con una conversione definita dall'utente.

    • Per B(const B&), abbiamo bisogno di inizializzare un const B& che ci porta a http://eel.is/c++draft/over.ics.list#8 poi a http://eel.is/c++draft/over.ics.ref#2 (con l'aiuto di http://eel.is/c++draft/dcl.init#dcl.init.list-3 "In caso contrario, se T è un tipo di riferimento, un prvalue temporanea del tipo a cui fa riferimento T è copiare-list -initialized ... ") quindi a http://eel.is/c++draft/over.best.ics#over.ics.list-6. Il contesto OR risultante ha candidati B(A); e B(const B&), con l'argomento 0. Questo è il nostro secondo contesto OR ed è l'inizializzazione della copia-lista di 13.3.1.7 (come richiesto da over.ics.ref # 2 e dcl.init.list-3).

      • Per B(A), il costruttore è esplicito e quindi ignorato dal Clang (in contraddizione con le specifiche), ma accettata da GCC (da qui l'ambiguità).

      • Per B(const B&), questo è lo scenario gestito da @Columbo e pertanto la conversione definita dall'utente che sarebbe necessaria è vietata. Le bozze più recenti non hanno più questa regola (ma probabilmente sarà aggiunta nuovamente). Ma poiché la conversione da 0 a const B& sarebbe una normale conversione definita dall'utente (non un'inizializzazione di elenchi), ignorerebbe comunque il costruttore esplicito necessario per la conversione (per questo potenziale secondo utilizzo del costruttore di copie) e quindi l'utente definito la conversione non sarebbe comunque possibile e la regola è molto meno importante di quanto pensassi quando ho scritto il riassunto più breve sopra riportato.

Quindi per GCC si può utilizzare il costruttore esplicito direttamente, e in aggiunta da un singolo uso del costruttore di copie. Per clang, considera solo l'uso diretto del costruttore esplicito, e non lo userà indirettamente da una inizializzazione della copia-lista usando il costruttore di copie come GCC. Entrambi non prenderanno in considerazione l'utilizzo del costruttore di copie una seconda volta, ed è irrilevante qui.

2

La corretta semantica lista di inizializzazione è

B b{0}; 

che compila bene. Se scrivi B b({0});, gcc non può decidere se chiamare direttamente B(A) o creare B ({0}) e quindi copiarlo con B(const B&) nella seconda fase. Non esiste un ordine di priorità tra queste due opzioni.

È un problema di lingua, non un problema di compilazione. Vedi questo gcc bug report.