2012-05-26 9 views
22

In diversi posti che ho visto le firme raccomandati di copiare e spostare i costruttori dato come:Un costruttore di spostamenti deve prendere un riferimento const o non const?

struct T 
{ 
    T(); 
    T(const T& other); 
    T(T&& other); 
}; 

Dove il costruttore di copia prende un riferimento const, e il costruttore mossa prende un riferimento rvalue non-const.

Per quanto posso vedere, però, questo impedisce mi approfittando della semantica mossa quando il ritorno oggetti const da una funzione, come ad esempio nel caso di seguito:

T generate_t() 
{ 
    const T t; 
    return t; 
} 

Testing questo con VC11 Beta, T ' Viene chiamato il costruttore di copie s e non il costruttore di move. Anche usando return std::move(t); il costruttore di copie viene ancora chiamato.

Posso vedere come questo ha senso, dal momento che t è const, quindi non dovrebbe legarsi a T&&. Utilizzare const T&& nella firma del costruttore di movimento funziona correttamente e ha senso, ma il problema è che poiché other è const, non è possibile annullare i suoi membri se devono essere annullati, funzionerà solo quando tutti i membri sono scalari o hanno costruttori di movimento con la firma giusta.

Sembra l'unico modo per assicurarsi che il costruttore di movimento sia chiamato nel caso generale per aver fatto t non-const, ma non mi piace farlo - le cose costanti sono buone e io non si aspetterebbe che il client di T sapesse che dovevano andare contro quel modulo per aumentare le prestazioni.

Quindi, credo che la mia domanda sia duplice; per prima cosa, un costruttore di mosse deve prendere un riferimento al valore const o non const? E secondo: ho ragione in questa linea di ragionamento? Che dovrei smettere di restituire cose che sono costanti?

+2

Bene, penso che i casi d'uso di restituzione di const temporaries siano piuttosto piccoli. Se lo dichiari in anticipo, allora perché vuoi fare alcune cose con esso prima di tornare. Se no, allora un semplice 'return T()' farebbe comunque. Anche se posso ancora vedere alcuni casi d'uso, penso che siano rari. E, naturalmente, un riferimento di valore da cui non è possibile rubare risorse non ha alcun valore rispetto a un riferimento di lvalue. Domanda interruttiva, comunque. –

+1

A proposito, questo codice non è idoneo per NRVO? Quindi, ciò che "dovrebbe" accadere è che l'oggetto 'const'' t' è costruito nella stessa posizione del valore di ritorno non-const. Quindi, se necessario, il valore di ritorno non const * può * essere spostato dal chiamante tramite il costruttore di movimento non const (o un operatore di assegnazione movimento). Nel caso della costruzione, tuttavia, sarebbe di nuovo idoneo per la copia elision e 't' potrebbe essere costruito direttamente in qualunque cosa venga inizializzata usando una chiamata a' generate_t() '. Cosa sta impedendo a VC11 di realizzare questa ottimizzazione? –

+1

_consting things is good form_ non se si desidera modificarli, ad es. spostandoci da loro. –

risposta

18

Dovrebbe essere un riferimento di valore non const.

Se un oggetto viene inserito nella memoria di sola lettura, non è possibile sottrarre risorse da esso, anche se la sua durata formale sta terminando a breve. Gli oggetti creati come const in C++ sono autorizzati a vivere in memoria di sola lettura (utilizzando const_cast per provare a modificarli i risultati in un comportamento non definito).

+2

È probabile che questo non sia il caso. Un oggetto da cui è possibile spostare sembra indicare che gestisce le risorse, quindi la memoria deve essere scrivibile quando l'oggetto è costruito e probabilmente ha un distruttore che rilascia quelle risorse, quindi, di nuovo, dovrebbe essere scrivibile durante la distruzione. Dubito che il compilatore genererà il codice per gestire la marcatura della pagina di memoria come sola lettura dopo la costruzione e quindi contrassegnarla scrivibile prima della distruzione (e di sola lettura una volta completata la distruzione come molto probabilmente l'oggetto non è solo in quella pagina di memoria. .) Detto questo +1 –

+0

@David: "Dubito che ..." - vero, ma potrebbe essere utile funzionalità in modalità di debug, esp. quando si aggiusta il codice leg-const errato. Penso che il punto importante sia che anche se questa cosa non accadrà in realtà, il fatto che sia legale spiega perché non è possibile modificare gli oggetti const prima del loro distruttore (e in particolare non è possibile spostarli in modo efficiente dove tengono le risorse) . –

+0

@SteveJessop: Questo è il motivo per il +1: se l'oggetto è in memoria di sola lettura o meno non cambia il fatto che spostarsi da un oggetto costante sta rompendo il contratto: stai modificando qualcosa che hai promesso di non toccare. –

8

Un costruttore di spostamenti normalmente deve eseguire un riferimento non const.

Se fosse possibile spostarsi da un oggetto const, in genere implicherebbe che fosse altrettanto efficiente copiare un oggetto quanto "spostarlo" da esso. A questo punto normalmente non vi è alcun beneficio per avere un costruttore di mosse.

Si è anche corretto che se si dispone di una variabile da cui si intende passare, è necessario che sia non-const.

Come ho capito, questo è il motivo per cui Scott Meyers ha cambiato il suo consiglio sul restituire oggetti di tipo classe in base al valore delle funzioni per C++ 11. La restituzione di oggetti con il valore qualificato const impedisce la modifica involontaria di un oggetto temporaneo ma impedisce anche lo spostamento dal valore di ritorno.

5

Un costruttore di spostamenti deve prendere un riferimento const o non costante?

Si dovrebbero prendere riferimento rvalue non-const.I riferimenti razionali non hanno più senso nelle loro forme const semplicemente perché si desidera modificarli (in un modo, si desidera "" spostare ", si desidera il loro internals per sé).

Inoltre, sono stati progettati per essere utilizzati senza const e credo che il solo uso di un riferimento rvalue const è qualcosa di molto molto arcana che Scott Meyers citato in this parlare.

Ho ragione in questa linea di ragionamento? Che dovrei smettere di restituire cose che sono costanti?

Questa è una domanda troppo generica per rispondere. In questo contesto, penso che valga la pena menzionare che esiste la funzionalità std::forward che conserva sia il valore di rvalore e valore di lvalore che la costante e eviterà anche la creazione di un temporaneo come farebbe una funzione normale nel caso si restituisse qualcosa passato a esso.

Questo ritorno potrebbe causare anche il rvalue di riferimentodi essere "storpiato" in lvalue riferimento e in genere non si vuole che, di conseguenza, l'inoltro perfetta con la funzionalità di cui sopra risolve il problema.

Detto questo, ti suggerisco di dare un'occhiata al discorso sul quale ho postato un link.