web-dev-qa-db-fra.com

Manière la plus concise de désactiver la copie et de déplacer la sémantique

Ce qui suit fonctionne sûrement mais est très fastidieux:

T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

J'essaie de découvrir la manière la plus concise. Les éléments suivants fonctionneront-ils?

T& operator=(T) = delete;

Mise à jour

Notez que je choisis T& operator=(T) au lieu de T& operator=(const T&) ou T& operator=(T&&), car il peut servir les deux objectifs.

17
Lingxi

Selon ce tableau (par Howard Hinnant):

meow

La manière la plus concise est de =delete opérateur d'affectation de déplacement (ou constructeur de déplacement, mais cela peut provoquer les problèmes mentionnés dans les commentaires).

Cependant, à mon avis, la façon la plus lisible est de =delete à la fois constructeur de copie et opérateur d'affectation de copie.

27
HolyBlackCat

Vous pouvez écrire un simple struct et en hériter:

struct crippled
{
    crippled() = default;

    crippled(const crippled&) = delete;
    crippled(crippled&&) = delete;

    crippled& operator=(const crippled&) = delete;
    crippled& operator=(crippled&&) = delete;
};

Usage:

struct my_class : crippled
{

};

int main()
{
    my_class a;
    auto b = a; // fails to compile
}
5
Vittorio Romeo

Je préfère hériter de boost :: noncopyable , rendant ainsi l'intention immédiatement claire et déléguant les détails à une bibliothèque de confiance.

#include <boost/core/noncopyable.hpp>

class X: private boost::noncopyable
{
};

Cela implique d'ajouter une dépendance, mais si vous êtes d'accord avec cela, c'est sans doute un moyen très concis et expressif de l'accomplir.

4
ricab

N'essayez pas de trouver "le moyen le plus concis" pour écrire un morceau de code.

S'il n'y a pas une forme évidente d'exprimer quelque chose qui est également très concis - ne cherchez pas, n'essayez pas de vous familiariser avec la langue pour écrire quelques caractères de moins. Pourquoi? Pensez aux gens qui lisent votre code: si vous avez besoin de consulter la norme pour réaliser que votre code fait ce que vous voulez qu'il fasse - les lecteurs de votre code le feront aussi. Sauf qu'ils ne sauront pas ce que vous essayez d'accomplir; afin qu'ils ne consultent pas la norme; donc ils seront juste confus sur ce que fait votre code. Ou - certains l'obtiendront et certains ne le feront pas.*

Dans votre cas, si vous effectuez un sous-ensemble de ces suppressions, ou utilisez une autre astuce "intelligente" - en tant que personne lisant votre code, il est peu probable que je ne comprenne pas, sans remarquer que vous essayez réellement d'obtenir toutes les copies et déplacer la sémantique supprimée. Et je serai confus, pensant que vous essayez de faire autre chose. En fait, si j'étais vous, j'envisagerais même d'ajouter un commentaire disant:

/* Disabling copy and move semantics because XYZ */
T(const T&) = delete;
T(T&&) = delete;
T& operator=(const T&) = delete;
T& operator=(T&&) = delete;

ce qui est encore plus "fastidieux", mais rendrait votre intention/motivation absolument claire pour vos futurs lecteurs.

Il y a aussi la question de savoir quelle est exactement la raison "XYZ". Certains diront qu'il n'y a aucune bonne raison de supprimer les membres du mouvement, et c'est généralement une mauvaise idée de le faire. Le luminaire C++ Howard Hinnant a ceci pour dire sur la question.

* - Une variante d'un principe que j'ai énoncé ici .

3
einpoklum

Je pense que dans ce cas, les macros sont en fait plus lisibles:

#define NOT_COPYABLE( TypeName ) \
TypeName ( TypeName const& ) = delete; \
TypeName & operator = ( TypeName const& ) = delete;

#define NOT_MOVEABLE( TypeName ) \
TypeName ( TypeName && ) = delete; \
TypeName & operator = ( TypeName && ) = delete;
0
KevinZ