2015-04-05 15 views
5

si prega di guardare il seguente codice di esempio:Perché viene chiamato il costruttore della copia invece del costruttore di movimento?

#include <iostream> 

    struct Foo { 
     Foo() { std::cout << "Default!\n"; } 
     Foo(const Foo& foo) { std::cout << "Copy!\n"; } 
     Foo(Foo&& foo) { std::cout << "Move!\n"; } 
    }; 

    struct Bar { 
     Foo foo; 
     Bar() {} 
     Bar(Bar &that) : foo(that.foo) {} 
     Bar(Bar &&that) : foo(std::move(that.foo)) {} 
    }; 

    Bar f() { 
     Bar bar; 
     return bar; 
    } 

    int main() { 
     Bar bar(f()); 
    } 

I'am aspettava che l'uscita di questo codice dovrebbe essere:

Default! 
Move! 

ma quello che ottengo è:

Default! 
Copy! 

Non riesco a vedere alcun motivo per cui venga chiamato il costruttore di copia anziché il costruttore di mosse. Se inserisco la parola chiave const davanti a Bar &that nella dichiarazione del costruttore di copie di struct Bar, ho ottenuto il risultato corretto. So che è meglio per molti casi prendere un riferimento a lvalue piuttosto che un semplice riferimento a lvalue per i costruttori di copie, ma voglio solo sapere il motivo per cui ciò è accaduto.

Perché in questo esempio è stato preferito Bar & su Bar && anche se il valore di ritorno di f() deve essere considerato come un valore di prvalore? Perché la parola chiave const risolve il problema? const risolve davvero il problema? Ha qualcosa a che fare con RVO (Return Value Optimization)? Oppure, questo è solo un bug del compilatore?

Ho testato questo esempio su Visual C++ novembre 2012 CTP.

ho trovato un problema simile qui:

Copy constructor is being called instead of the move constructor

Ma ancora non riesco a capire perché.

Qualcuno può aiutarmi?

+0

Impossibile riprodurre. Con GCC ho appena ottenuto "Default!" (Sano NRVO al lavoro), con '-fno-elide-constructors' ottengo l'atteso' Default! Mossa! Spostare! '. Forse un compilatore b^Hshortcoming? –

+0

cambia 'Bar (Bar & that)' a 'Bar (const bar & that)'. –

+0

@ KerrekSB 1. Capisco il caso di NRVO. Ma perché due piuttosto che un solo "Move!" È previsto quando la copia elision è disattivata? 2. Quindi stai dicendo che questo è solo un bug del compilatore che ho usato? Grazie comunque. –

risposta

3

Wow, quando compilo questo con ...

  1. Visual Studio in Debug vedo "Default! Copia!".
  2. Visual Studio in Release I vedere "Predefinito!".
  3. Se si modifica Bar(Bar &that) in Bar(const Bar &that) quindi "Predefinito! Sposta!"
  4. Sorprendentemente, se si cambia l'ordine di Bar(Bar &that) con Bar(Bar &&that) (in modo che il cursore di spostamento sia definito per primo), in effetti verrà visualizzato "Predefinito! Sposta!"
+1

Wow! Ho anche provato me stesso, 1, 2 e 3, ma 4 è davvero sorprendente! Grazie. Quindi hai qualche idea sul motivo per cui accadono queste cose? –

+2

OK, il numero 4 stabilisce in modo definitivo che si tratta di un bug del compilatore. –

+0

Per essere corretti, 'f()' dovrebbe restituire 'std :: move (bar)'. – Mohammad

-2

La tua domanda è probabilmente risposta here.

Un oggetto temporaneo non può associare a un riferimento non const. Il costruttore di copie deve prendere come riferimento un oggetto const per poter fare copie di oggetti temporanei.

L'altra cosa è che gli oggetti temporanei non devono essere modificabili in quanto devono essere distrutti in qualsiasi momento. Tenere un riferimento ai provvisori porta a potenziali modifiche su oggetti inesistenti per incuria.

+0

Quella citazione non ha nulla a che fare con il comportamento osservato. –

+1

@ T.C .: Quella citazione ha tutto a che fare con esso. Non solo il costruttore di copie dovrebbe essere una corrispondenza peggiore, non dovrebbe nemmeno far parte del set candidato. –

+1

@BenVoigt Certo, ma questo non spiega nulla. La domanda è "perché è stato chiamato?", Non "perché non è stato chiamato?" –

4

È solo la solita non conformità di Visual C++ che consente di associare un riferimento non costante a un valore temporaneo.Violenta le regole del linguaggio, ma è passato troppo tempo prima di essere catturato, quindi ora c'è il codice che dipende dal bug che si interromperebbe se fosse corretto.

Questo comportamento che consente erroneamente il costruttore di copie non const da utilizzare, combinato con il supporto del rvalue di riferimento incompleto in quella versione di Visual C++, provoca evidentemente il sovraccarico errato selezionato.

Se si desidera utilizzare le funzionalità di C++ 11/C++ 14, è consigliabile rimanere in cima all'ultima versione di Visual C++.

Problemi correlati