2011-11-03 15 views
62

Ho un std::vector di oggetti di una determinata classe A. La classe non è banale e ha i costruttori di copia e definiti costruttori di movimento.Come applicare la semantica del movimento quando un vettore cresce?

std::vector<A> myvec; 

Se riempio-up il vettore con A oggetti (usando per esempio myvec.push_back(a)), il vettore sarà crescere in dimensioni, utilizzando il costruttore di copia A(const A&) per creare un'istanza di nuove copie degli elementi nel vettore.

Posso in qualche modo imporre che viene utilizzato il costruttore di spostamenti della classe A?

+4

È possibile, utilizzando un'implementazione vettoriale in base al movimento. –

+1

Puoi per favore essere un po 'più specifico su come raggiungere questo obiettivo? –

+1

È sufficiente utilizzare un'implementazione vettoriale in base al movimento. Sembra che la tua implementazione della libreria standard (che è btw?) Non sia sensibile allo spostamento. Potresti provare con contenitori sensibili al movimento di Boost. –

risposta

84

È necessario informare il C++ (in particolare std::vector) che il costruttore e il distruttore di movimento non lanciano, utilizzando noexcept. Quindi il costruttore di movimento verrà chiamato quando il vettore cresce.

Questo è come dichiarare e implementare un constuctor mossa che viene rispettato da std::vector:

A(A && rhs) noexcept { 
    std::cout << "i am the move constr" <<std::endl; 
    ... some code doing the move ... 
    m_value=std::move(rhs.m_value) ; // etc... 
} 

Se il costruttore non è noexcept, std::vector non può usare, da allora non si può garantire l'eccezione garanzie richieste dallo standard.

Per di più su ciò che è detto nella norma, leggere C++ Move semantics and Exceptions

credito a Bo che lasciato intendere che essa può avere a che fare con le eccezioni. Considerare anche il consiglio di Kerrek SB e utilizzare emplace_back quando possibile. È può essere più veloce (ma spesso non lo è), può essere più chiaro e più compatto, ma ci sono anche alcuni problemi (soprattutto con costruttori non espliciti).

Modifica, spesso l'impostazione predefinita è ciò che si desidera: spostare tutto ciò che può essere spostato, copiare il resto. Per richiedere esplicitamente che, scrivere

A(A && rhs) = default; 

Facendo questo, si otterrà noexcept quando possibile: Is the default Move constructor defined as noexcept?

Si noti che le prime versioni di Visual Studio 2015 e più anziani non hanno sostenuto che, anche se sostiene spostare la semantica .

+11

Nota: 'throw()' è deprecato, usa invece 'noexcept'. –

+0

@MatthieuM. Ah, sì - risolto ora. Ben individuato. –

+0

Perplessità, in che modo "implora" l'impl "sapere se il cursore di movimento" value_type' è 'noexcept'? Forse la lingua limita la funzione call candidate set quando l'ambito di chiamata è anche una funzione 'noxcept'? –

14

È interessante notare che il vettore di gcc 4.7.2 utilizza solo il costruttore di movimento se sia il costruttore di movimento che il distruttore sono noexcept. Un semplice esempio:

struct foo { 
    foo() {} 
    foo(const foo &) noexcept { std::cout << "copy\n"; } 
    foo(foo &&) noexcept { std::cout << "move\n"; } 
    ~foo() noexcept {} 
}; 

int main() { 
    std::vector<foo> v; 
    for (int i = 0; i < 3; ++i) v.emplace_back(); 
} 

Emette l'atteso:

move 
move 
move 

Tuttavia, quando rimuovo noexcept dal ~foo(), il risultato è diverso:

copy 
copy 
copy 

Credo che questo risponde anche this question .

+0

Mi sembra che l'altra risposta parli solo del costruttore di mosse, non del _destructor_ che deve essere non eccetto. –

+2

Un buon punto, ma il distruttore non è eccetto per impostazione predefinita. –

+0

Beh, dovrebbe essere, ma come risulta, in gcc 4.7.2 non lo era. Quindi questo problema era, in effetti, specifico per gcc. Dovrebbe essere corretto in gcc 4.8.0, comunque. Vedi [domanda stackoverflow correlata] (http://stackoverflow.com/questions/15721544/destructors-and-noexcept). –

0

Sembra che l'unico modo (per C++ 17 e precedenti), per applicare std::vector l'uso della semantica di spostamento sulla riallocazione sia l'eliminazione del costruttore di copie :).In questo modo userà i tuoi costruttori di movimento o morirà provando, in fase di compilazione :).

Ci sono molte regole in cui std::vector NON DEVE utilizzare il costruttore di movimento su riallocazione, ma nulla su dove è DEVE UTILIZZARE esso.

template<class T> 
class move_only : public T{ 
public: 
    move_only(){} 
    move_only(const move_only&) = delete; 
    move_only(move_only&&) noexcept {}; 
    ~move_only() noexcept {}; 

    using T::T; 
}; 

Live

o

template<class T> 
struct move_only{ 
    T value; 

    template<class Arg, class ...Args, typename = std::enable_if_t< 
      !std::is_same_v<move_only<T>&&, Arg > 
      && !std::is_same_v<const move_only<T>&, Arg > 
    >> 
    move_only(Arg&& arg, Args&&... args) 
     :value(std::forward<Arg>(arg), std::forward<Args>(args)...) 
    {} 

    move_only(){} 
    move_only(const move_only&) = delete; 
    move_only(move_only&& other) noexcept : value(std::move(other.value)) {};  
    ~move_only() noexcept {}; 
}; 

Live code

La classe T deve avere noexcept mossa operatore costruttore/assigment e noexcept distruttore. Altrimenti riceverai un errore di compilazione.

std::vector<move_only<MyClass>> vec; 
+0

Non è necessario eliminare il costruttore di copie. Se il costruttore delle mosse non è eccetto, sarà usato. – balki

+0

@balki Può essere utilizzato. Lo standard non lo richiede ora. Ecco la discussione https://groups.google.com/a/isocpp.org/forum/?utm_medium=email&utm_source=footer#!msg/std-proposals/j5URs5ZY3GI/AanXG977CAAJ – tower120

Problemi correlati