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?
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.
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 .
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;
};
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 {};
};
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;