2013-06-10 7 views
10

Ho un modello come questo:std :: mossa nella lista di inizializzazione del costruttore nel modello di classe

template<typename T> 
struct foo { 
    T m_t; 
    foo(T t) : m_t(t) {} 
}; 

Il problema è che voglio supportare entrambi i tipi di piccole/regolari e enormi tipi (come matrici) per T. Mi consiglia scrivo la lista di inizializzazione del costruttore come questo

foo (T t) : m_t(std::move(t)) {} 

e esigere che il tipo T sostenere sempre la costruzione mossa, anche per i tipi più piccoli? Ci sono modi migliori?

risposta

11

e richiedono che il tipo T supporti sempre la costruzione del movimento, anche per i tipi più piccoli?

Qualsiasi tipo di copia costruibile è anche mobile costruibile. Spostarsi in questi casi chiama semplicemente il costruttore di copie. Pertanto, non c'è motivo non per utilizzare m_t(std::move(t)).

Un'alternativa è quella di usare i riferimenti invece:

foo (T const& t) : m_t(t) {} 
foo (T&& t) : m_t(std::move(t)) {} 

Questo ha il vantaggio di coinvolgere solo una costruzione piuttosto che due.

+0

Il tuo modo di riferimento è migliore del mio? Se T non è mossa costruibile è (o dovrebbe essere) implicito che T sia piccolo/normale (non grande), non è vero? In questo caso, pensi che un riferimento mi dia vantaggi di misurazione misurabili? – 7cows

+0

@ 7cows può fornire un vantaggio prestazionale misurabile quando T ha un costoso costruttore di copie e nessun costruttore di spostamenti, che è comune con le librerie C++ 03. Di solito di solito preferisco la tua strada. – Pubby

7

Sì, usare la mossa non ha svantaggi in quella situazione. Tutti gli oggetti copiabili sono automaticamente mobili, quindi non importa. In effetti, alcuni raccomandano di spostare sempre le variabili quando possibile, anche i numeri interi.

In alternativa, si può considerare di utilizzare l'inoltro perfetto, come descritto in this answer:

template <typename T2> 
foo(T2&& t) : m_t(std::forward<T2>(t)) {} 

Se si sa che T definisce un costruttore mossa veloce, non dovrebbe importare. In caso contrario, è consigliabile fornire un costruttore foo(const T&) per evitare copie non necessarie.

L'inoltro perfetto è solo una tecnica per raggiungerlo. La soluzione di Pubby per scrivere il costruttore foo(const T&) e foo(T&&) è, ovviamente, anche bene. I risultati sono gli stessi, è soprattutto una questione di stile.

Hai anche chiesto informazioni sui tipi di interi piccoli. In teoria, passarli per riferimento è più lento che copiarli, ma il compilatore dovrebbe essere in grado di ottimizzarlo su una copia, comunque. Non penso che farà la differenza.

Quindi, ottimizzare meglio nel caso peggiore in cui T può essere enorme e non fornisce un costruttore di spostamento veloce. Passare per riferimento è il migliore per quella situazione e non dovrebbe essere una cattiva scelta in generale.

+2

Il problema con l'inoltro perfetto è che accetterà * tutto * che il costruttore di 'T' accetta. – Pubby

+0

@Pubby Buon punto. Questo può essere positivo o negativo, a seconda del contesto. Se è necessario un maggior controllo sulle conversioni, entrambi i costruttori 'pippo' possono essere dichiarati come' espliciti'. –

+0

@Pubby, che potrebbe essere controllato con 'enable_if' e un controllo' is_same'. Certo, quando lo scrivi, probabilmente stai considerando lo stesso numero di linee che implementa esplicitamente entrambi i costruttori ... –

0

Il passaggio di valore ha il vantaggio di avere un solo costruttore (senza modello), ma ha il prezzo di una costruzione di spostamento aggiuntiva rispetto alle alternative di cui sopra.

Tuttavia, vi è un altro svantaggio non ancora citato sia del valore pass-by che della soluzione non modello fornita da Pubby: Lo spostamento non funzionerà se il costruttore di copie è definito come T(T&); (notare il riferimento a non-const).I riferimenti Rvalue possono associarsi a lvalue-references-to-const, ma non a lvalue-references-to-non-const, quindi il compilatore non può chiamare il costruttore di copie.

Per risolvere questo problema, è sufficiente aggiungere un terzo sovraccarico foo (T & t) : m_t(t) {} alla soluzione di Pubby.

Problemi correlati