À ma connaissance, move-constructors et move-assign doivent être marqués noexcept pour que le compilateur puisse les utiliser lors, par exemple, d’une réaffectation dans un vecteur.
Cependant, y a-t-il un cas réel où une construction-assignation, une construction-déménagement pourrait réellement lancer?
Mise à jour :
Les classes qui, par exemple, ont une ressource allouée lorsqu’elles sont construites ne peuvent pas être déplacées sans projection.
Cependant, existe-t-il un cas réel où un déplacement-assign, une construction-déménagement (ou swap) pourrait réellement lancer?
Oui. Considérons une implémentation de std::list
. L’iterator end
doit pointer “un dernier dernier élément” dans la liste. Il existe des implémentations de std::list
où l’ end
désigne un nœud alloué dynamicment. Même le constructeur par défaut alloue un tel nœud de sorte que lorsque vous appelez end()
, il y a quelque chose à pointer.
Dans une telle implémentation, chaque constructeur doit allouer un noeud pour que end()
pointe vers… même le constructeur du déplacement. Cette allocation peut échouer et lever une exception.
Ce même comportement peut s’étendre à tout conteneur basé sur un nœud.
Il existe également des implémentations de ces conteneurs basés sur des nœuds qui effectuent une optimisation “chaîne courte”: ils intègrent le nœud d’extrémité dans la classe de conteneur elle-même, au lieu d’une allocation dynamic. Ainsi, le constructeur par défaut (et le constructeur de mouvements) n’a rien à allouer.
L’opérateur d’affectation de mouvement peut lancer n’importe quel container
si la valeur de l’allocateur du conteneur est propagate_on_container_move_assignment::value
false, et si l’allocateur dans le lhs n’est pas égal à l’allocateur dans le rhs. Dans ce cas, il est interdit à l’opérateur d’atsortingbution de déplacement de transférer la propriété de la mémoire du rhs au lhs. Cela ne peut pas arriver si vous utilisez std::allocator
, car toutes les instances de std::allocator
sont égales les unes aux autres.
Mettre à jour
Voici un exemple conforme et portable du cas où propagate_on_container_move_assignment::value
est false. Il a été testé sur la dernière version de VS, gcc et clang.
#include #include #include #include template class allocator { int id_; public: using value_type = T; allocator(int id) noexcept : id_(id) {} template allocator(allocator const& u) noexcept : id_(u.id_) {} value_type* allocate(std::size_t n) { return static_cast(::operator new (n*sizeof(value_type))); } void deallocate(value_type* p, std::size_t) noexcept { ::operator delete(p); } template friend bool operator==(allocator const& x, allocator const& y) noexcept { return x.id_ == y.id_; } }; template bool operator!=(allocator const& x, allocator const& y) noexcept { return !(x == y); } template using vector = std::vector>; struct A { static bool time_to_throw; A() = default; A(const A&) {if (time_to_throw) throw 1;} A& operator=(const A&) {if (time_to_throw) throw 1; return *this;} }; bool A::time_to_throw = false; int main() { vector v1(5, A{}, allocator{1}); vector v2(allocator{2}); v2 = std::move(v1); try { A::time_to_throw = true; v1 = std::move(v2); assert(false); } catch (int i) { std::cout << i << '\n'; } }
Ce programme génère:
1
ce qui indique que le vector
, l'opérateur d'affectation de déplacement, copie / déplace ses éléments lorsque propagate_on_container_move_assignment::value
est défini sur false et que les deux allocateurs en question ne se comparent pas égaux. Si l'un de ces copies / déplacements est lancé, l'affectation de déplacement de conteneur est levée.
Oui, les constructeurs de mouvements de lancer existent dans la nature. Considérons std::pair
où T
peut pas être déplacé, et U
est uniquement copiable (supposons que des copies puissent être jetées). Ensuite, vous avez un constructeur de mouvements std::pair
utile qui peut lancer.
Si vous avez besoin d’un utilitaire std::move_if_noexcept
dans la bibliothèque standard (utile pour implémenter std::vector::resize
avec au moins la garantie d’exception de base).
Voir aussi Move constructors et la garantie d’exception forte
Déplacer des constructeurs sur des classes avec des membres de données const
peut également lancer. Vérifiez ici pour plus d’informations.