Confusione nell’inizializzazione della copia e nell’inizializzazione diretta

Prendi in considerazione una semplice dichiarazione (tratto da C’è una differenza in C ++ tra l’inizializzazione della copia e l’inizializzazione diretta? ):

A c2 = A(); 

Questo valore di istruzione-inizializza un temporaneo e quindi copia quel valore in c2 (Leggi 5.2.3 / 2 e 8.5 / 14). Ciò richiederà ovviamente un costruttore di copie non esplicito (leggi 8.5 / 14 e 12.3.1 / 3 e 13.3.1.3/1)

[Considera la frase in grassetto sopra para] -> La mia domanda è perché?

Ora considera questo codice:

 class B {}; struct A { A(B const&) {} A(A const&) = delete; //A(A const&); //delete above statement and uncomment this statement, //and everything works, even though there in no body of copy constructor Oo }; A a2 = B(); //error since there is no copy constructor oO 

Perché l’inizializzazione della copia richiede la presenza del costruttore di copie anche se non è necessario a volte come presentato nel codice precedente

Per favore, per favore un’altra cosa :

Mentre l’inizializzazione diretta dispone di tutti i costruttori disponibili per la chiamata, e inoltre può eseguire qualsiasi conversione implicita per abbinare i tipi di argomenti, l’inizializzazione della copia può semplicemente impostare una sequenza di conversione implicita .

[ Attenzione agli audaci nel seguente paragrafo ]

Ciò non significa che l’inizializzazione diretta ha accesso a tutti i costruttori e può eseguire una sequenza di conversione implicita , mentre l’inizializzazione della copia che tutto ciò può fare è eseguire una sequenza di conversione implicita ? . Quello che voglio dire è che la conversione implicita nell’inizializzazione diretta è diversa dalla sequenza di conversione implicita nell’inizializzazione della copia?

Le regole per la valutazione

 A a1 = B(); // (1) copy-initialization A a2 = {B()}; // (2) copy-initialization A a3{B()}; // (3) direct-initialization 

vieni da [dcl.init] / 17 :

– Se l’inizializzatore è un elenco di parentesi graffe (non tra parentesi), l’object o il riferimento è inizializzato in elenco (8.5.4).
– […]
– Se il tipo di destinazione è un tipo di class (possibilmente qualificato cv):

  • Se l’inizializzazione è diretta-inizializzazione, o se è l’inizializzazione della copia in cui la versione cv -qualificata del tipo sorgente è la stessa class di, o una class derivata di, la class della destinazione, vengono considerati i costruttori. […]
  • Altrimenti (vale a dire, per i restanti casi di inizializzazione della copia), le sequenze di conversione definite dall’utente che possono convertire dal tipo sorgente al tipo di destinazione o (quando viene utilizzata una funzione di conversione) a una class derivata vengono enumerate come descritto in 13.3. 1.4, e il migliore viene scelto tramite la risoluzione di sovraccarico (13.3). […] Il risultato della chiamata (che è il temporaneo per il caso del costruttore) viene quindi utilizzato per l’inizializzazione diretta, in base alle regole precedenti, dell’object che è la destinazione dell’inizializzazione della copia. In certi casi, è consentita l’implementazione di eliminare la copia inerente a questa inizializzazione diretta costruendo il risultato intermedio direttamente nell’object da inizializzare; vedi 12.2, 12.8.

Sia per a2 che a3 , l’inizializzatore è un elenco di init rinforzato , quindi eseguiamo solo l’inizializzazione dell’elenco. Questo finisce per chiamare B const& constructor.

Per a1 , il primo elemento secondario non si applica, poiché il tipo di origine ( B ) non è la stessa class derivata del tipo di destinazione ( A ). Quindi entriamo nel secondo punto sotto-punto che implica la considerazione delle funzioni di conversione. Ce n’è uno ( A(B const&) ) in modo da riscrivere efficacemente l’espressione

 A a1_new{A{B{}}}; 

Ora in genere, questa copia extra verrà eliminata. Ma lo stai proibendo esplicitamente, quindi il codice non può essere compilato.


Per quanto riguarda il motivo della differenziazione? Non lo so. Sembra che l’inizializzazione della copia dovrebbe essere semplicemente lo zucchero sintattico per l’inizializzazione diretta. Nella maggior parte dei casi, dopo tutto, è …

(Quanto segue si applica a C ++ 11) Per evitare vagabondaggi troppo, A a2 = B(); ha due fasi: innanzitutto, viene creato un object temporaneo di tipo B quando si scrive B() . Quindi, la funzione A(B const&) {} non verrà invocata qui con il ruolo di inizializzazione diretta A poiché si utilizza la syntax di inizializzazione della copia. Se vuoi invocarlo per inizializzare direttamente A, dovresti scrivere A a2(B()) invece di usare la syntax di inizializzazione della copia (il diavolo qui è = ). Allora, qual è il prossimo? Si ottiene un object temporaneo di tipo B e si desidera utilizzarlo per inizializzare obj di tipo A , e ora si inizializza indirettamente A convertendo B() nel tipo A Quindi la tua funzione A(B const&) {} è usata come conversione di tipo piuttosto che inizializzare direttamente A.

A causa della conversione, viene creato un object temporaneo di tipo A, e quindi abbiamo bisogno di un costruttore di copie per inizializzare a2 usando quell’object temporaneo.

Nell’inizializzazione della copia, non è ansible chiamare le funzioni di conversione del tipo con explicit parola chiave explicit . La filosofia di design della parola chiave explicit è che dovresti invocarla direttamente. In combinazione con le funzioni di conversione del tipo, quelle funzioni attribuite explicit non devono essere utilizzate per la conversione del tipo implicito. Nell’inizializzazione diretta, possono essere tutti definiti anche se sono explicit .

Per rendere le cose interessanti, se si crea il costruttore di copia e si sposta explicit costruttore, non si può nemmeno scrivere T obj = T{} , ma si può scrivere T obj {T{}} . Puoi anche copiare e spostare ctor come conversione, solo la destinazione e il tipo di sorgente sono della stessa class.

Vorrei fornirti ulteriori letture qui . Dopodiché, leggi questa domanda per sapere sull’inizializzazione delle liste di copia e questa domanda per informazioni sui tipi di aggregazione.