Il costruttore di mosse ha chiamato dopo aver invocato una funzione di conversione?

Considera questo esempio:

struct T { }; struct S { operator T(); }; S s; T t = s; 

[dcl.init] ci porterà a [over.match.copy] che troverà l’ operator T() funzione di conversione operator T() . Ma abbiamo finito a quel punto, o dovremmo invocare T(T&& rhs) , vincolare i rhs al ritorno operator T() tramite [dcl.init.ref]? Ci sono differenze per quanto riguarda la risposta a questa domanda tra C ++ 11 e C ++ 1z?

Questo cade sotto [dcl.init] /17.6.3 , che è abbastanza chiaro su cosa succede dopo la risoluzione del sovraccarico seleziona la funzione di conversione:

La funzione selezionata viene chiamata con l’espressione initializer come argomento; se la funzione è un costruttore, la chiamata è un valore della versione cv -qualificata del tipo di destinazione il cui object risultato è inizializzato dal costruttore. La chiamata viene utilizzata per inizializzare direttamente, in base alle regole precedenti, l’object che è la destinazione dell’inizializzazione della copia.

Nel tuo caso questo a sua volta ricorre in [dcl.init] /17.6.1 :

Se l’espressione initializer è un valore prvalue e la versione cv -qualificata del tipo sorgente è della stessa class della class della destinazione, l’espressione initializer viene utilizzata per inizializzare l’object di destinazione.


In C ++ 11 il secondo passo richiama un costruttore di movimento, poiché non ha il proiettile corrispondente a 17.6.1 di C ++ 17. Invece fai di nuovo la danza di inizializzazione diretta / di sovraccarico :

Se l’inizializzazione è diretta-inizializzazione, […] vengono considerati i costruttori. I costruttori applicabili sono enumerati ([over.match.ctor]), e il migliore viene scelto tramite la risoluzione di sovraccarico ([over.match]). Il costruttore così selezionato viene chiamato per inizializzare l’object, con l’espressione di inizializzazione o elenco di espressioni come argomento (i). Se nessun costruttore si applica, o la risoluzione di sovraccarico è ambigua, l’inizializzazione è mal formata.

Questa mossa può (e in pratica sarà) eluita; vedi [class.copy] / 31 .


Il caso più interessante è in realtà

 T t(s); 

che sotto la dicitura C ++ 17 è effettivamente richiesto per chiamare un costruttore di mosse, perché usa la regola di inizializzazione diretta e fa la risoluzione di sovraccarico sui costruttori di T Questo seleziona il costruttore di movimento di T e lo chiama per inizializzare t , convertendo s in un valore di T che è materializzato in un temporaneo e legato al parametro del costruttore di movimento. Il punto 17.6.1 è semplicemente non raggiungibile nel processo e il punto elenco in C ++ 11 [class.copy] / 31 (ora [class.copy.elision] / 1 ) che consentiva l’elisione in questo scenario è stato rimosso in C ++ 17.

Questo è molto probabilmente un difetto.