web-dev-qa-db-fra.com

Comment appliquer la sémantique de déplacement lorsqu'un vecteur grandit?

J'ai un std::vector D'objets d'une certaine classe A. La classe n'est pas triviale et a des constructeurs de copie et les constructeurs de déplacement sont définis.

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 de les éléments du vecteur.

Puis-je faire en sorte que le constructeur de déplacement de la classe A soit utilisé à la place?

80

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

Voici comment déclarer et implémenter un constuctor de déplacement 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 alors pas garantir les garanties d'exception exigées par la norme.

Pour en savoir plus sur ce qui est dit dans la norme, lisez Sémantique et exceptions C++ Move

Nous remercions Bo qui a laissé entendre que cela pourrait avoir à voir avec des exceptions. Tenez également compte des conseils de Kerrek SB et utilisez emplace_back quand c'est possible. Il peut être plus rapide (mais ce n'est souvent pas le cas), il peut être plus clair et plus compact, mais il y a aussi quelques pièges (en particulier avec les constructeurs non explicites).

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 si possible: Le constructeur Move par défaut est-il défini comme noexcept?

Notez que les premières versions de Visual Studio 2015 et antérieures ne prenaient pas en charge cela, même s'il prend en charge la sémantique de déplacement.

111
Johan Lundberg

Fait intéressant, le vecteur de gcc 4.7.2 n'utilise le constructeur de déplacement que si le constructeur de déplacement 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();
}

Cela produit les résultats attendus:

move
move
move

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

copy
copy
copy

Je suppose que cela répond aussi cette question .

17
Nikola Benes