Comment appliquer la sémantique des mouvements lorsqu'un vecteur croît?

j'ai un std::vector des objets d'une certaine classe A . La classe n'est pas triviale et a défini les constructeurs de copie et .

std::vector<A>  myvec;

si je remplis le vecteur avec des objets A (en utilisant par exemple myvec.push_back(a) ), le vecteur augmentera de taille, en utilisant le constructeur de copie A( const A&) pour instancier de nouvelles copies des éléments dans le vecteur.

puis-je d'une manière ou d'une autre faire respecter le constructeur de mouvements de la classe A est utilisé à la place?

70
demandé sur ネロク 2011-11-04 01:03:27

3 réponses

vous devez informer C++ (spécifiquement std::vector ) que votre constructeur de mouvements et destructeur ne lance pas, en utilisant noexcept . Ensuite, le constructeur de déplacement sera appelée lorsque le vecteur grandit.

c'est comment déclarer et mettre en œuvre un moteur de déménagement qui est respecté par 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...
}

si le constructeur n'est pas noexcept , std::vector ne peut pas l'utiliser, car il ne peut pas assurer les garanties d'exception exigé par la norme.

pour en savoir plus sur ce qui est dit dans la norme, lire C++ sémantique de Déplacement et Exceptions

Crédit à la Bo qui a laissé entendre qu'il peut avoir à faire avec des exceptions. Prenez également en considération les conseils de Kerrek SB et utilisez emplace_back si possible. Il peut être plus rapide (mais n'est pas souvent), il peut être plus clair et plus compact, mais il ya aussi quelques pièges (en particulier avec non-explicite constructeur.)

Edit , souvent la valeur par défaut est ce que vous voulez: déplacer tout ce qui peut être déplacé, copier le reste. Pour demander explicitement cela, écrivez

A(A && rhs) = default;

en faisant cela, vous obtiendrez noexcept quand c'est possible: est-ce que le constructeur de déplacement par défaut est défini comme noexcept?

notez que les premières versions de Visual Studio 2015 et plus anciennes ne supportaient pas cela, même si elle supporte la sémantique des mouvements.

93
répondu Johan Lundberg 2018-02-13 19:46:19

il est intéressant de noter que le vecteur de gcc 4.7.2 n'utilise le constructeur de mouvements que si le constructeur de mouvements et le destructeur sont tous deux noexcept . Un exemple simple:

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();
}

ce qui produit les résultats attendus:

move
move
move

cependant, quand je supprime noexcept de ~foo() , le résultat est différent:

copy
copy
copy

je suppose que cela répond aussi cette question .

14
répondu Nikola Benes 2017-12-26 17:55:10

il semble que la seule façon (pour C++17 et early), d'appliquer std::vector utiliser la sémantique de déplacement sur la réallocation est de supprimer le constructeur de copie :) . De cette façon, il utilisera vos constructeurs move ou die trying, au moment de la compilation :).

il y a de nombreuses règles où std::vector ne doit pas utiliser move constructor sur la réallocation, mais rien au sujet de l'endroit où il doit utiliser il.

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

ou

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

votre classe T doit avoir noexcept move constructor/assignment operator et noexcept destructor. Sinon, vous aurez une erreur de compilation.

std::vector<move_only<MyClass>> vec;
0
répondu tower120 2017-10-30 16:09:54