En C ++ 11, nous pouvons écrire ce code:
struct Cat {
Cat(){}
};
const Cat cat;
std::move(cat); //this is valid in C++11
quand j'appelle std::move
, cela signifie que je veux déplacer l'objet, c'est-à-dire que je vais changer l'objet. Déplacer un const
objet n'est pas raisonnable, alors pourquoi ne std::move
restreint-il pas ce comportement? Ce sera un piège dans le futur, non?
Ici, piège signifie comme Brandon l'a mentionné dans le commentaire:
"Je pense qu'il veut dire qu'il" le piège "sournoisement parce que s'il ne réalise pas, il finit avec une copie qui n'est pas ce qu'il voulait."
Dans le livre 'Effective Modern C ++' de Scott Meyers, il donne un exemple:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) //here we want to call string(string&&),
//but because text is const,
//the return type of std::move(text) is const std::string&&
//so we actually called string(const string&)
//it is a bug which is very hard to find out
private:
std::string value;
};
S'il std::move
était interdit d'opérer sur un const
objet, nous pourrions facilement trouver le bogue, non?
std::move
par lui-même ne fait rien à l'objet. On pourrait dire qu'ilstd::move
est mal nommé.CAT cat2 = std::move(cat);
, en supposant qu'ilCAT
prend en charge l'assignation de déplacement régulière.std::move
est juste un casting, il ne bouge en fait rienRéponses:
ici, nous voyons une utilisation de
std::move
sur unT const
. Il renvoie unT const&&
. Nous avons un constructeur de mouvement pourstrange
qui prend exactement ce type.Et ça s'appelle.
Maintenant, il est vrai que ce type étrange est plus rare que les bogues que votre proposition résoudrait.
Mais, d'un autre côté, l'existant
std::move
fonctionne mieux dans le code générique, où vous ne savez pas si le type avec lequel vous travaillez est aT
ou aT const
.la source
std::move
sur unconst
objet.const T&&
. Cela exprime un "protocole API" du genre "Je prendrai un rvalue-ref mais je promets que je ne le modifierai pas". Je suppose que, mis à part lors de l'utilisation de mutable, c'est rare. Peut-être qu'un autre cas d'utilisation est de pouvoir l'utiliserforward_as_tuple
sur presque tout et de l'utiliser plus tard.Il y a un truc ici que vous oubliez, à savoir qui
std::move(cat)
ne bouge en fait rien . Il dit simplement au compilateur d' essayer de se déplacer. Cependant, comme votre classe n'a pas de constructeur qui accepte aconst CAT&&
, elle utilisera à la place leconst CAT&
constructeur de copie implicite et copiera en toute sécurité. Aucun danger, aucun piège. Si le constructeur de copie est désactivé pour une raison quelconque, vous obtiendrez une erreur de compilation.imprime
COPY
, nonMOVE
.http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f
Notez que le bogue dans le code que vous mentionnez est un problème de performances , pas un problème de stabilité , donc un tel bogue ne provoquera jamais de plantage. Il utilisera simplement une copie plus lente. De plus, un tel bogue se produit également pour les objets non const qui n'ont pas de constructeur de déplacement, donc simplement ajouter une
const
surcharge ne les attrapera pas tous. Nous pourrions vérifier la possibilité de déplacer la construction ou de déplacer l'assignation du type de paramètre, mais cela interférerait avec le code de modèle générique qui est censé se rabattre sur le constructeur de copie. Et diable, peut-être que quelqu'un veut pouvoir construire à partir deconst CAT&&
qui suis-je pour dire qu'il ne peut pas?la source
const
n'aidera pas non plus. [class.copy] §8: "Sinon, le constructeur de copie implicitement déclaré aura la formeX::X(X&)
"L'une des raisons pour lesquelles le reste des réponses a négligé jusqu'à présent est la capacité du code générique à être résilient face au mouvement. Par exemple, disons que je voulais écrire une fonction générique qui déplaçait tous les éléments d'un type de conteneur pour créer un autre type de conteneur avec les mêmes valeurs:
Cool, maintenant je peux créer relativement efficacement un à
vector<string>
partir d'undeque<string>
et chaque individustring
sera déplacé dans le processus.Mais que faire si je veux passer d'un
map
?Si on
std::move
insiste sur un non-const
argument, l'instanciation de ci-dessusmove_each
ne se compilera pas car elle essaie de déplacer aconst int
(thekey_type
of themap
). Mais ce code ne se soucie pas s'il ne peut pas déplacer le fichierkey_type
. Il souhaite déplacer lemapped_type
(std::string
) pour des raisons de performances.C'est pour cet exemple, et d'innombrables autres exemples comme celui-ci dans le codage générique, qui
std::move
est une demande de déplacement , pas une demande de déplacement.la source
J'ai la même inquiétude que l'OP.
std :: move ne déplace pas un objet, ni ne garantit que l'objet est mobile. Alors pourquoi s'appelle-t-il bouger?
Je pense qu'être non mobile peut être l'un des deux scénarios suivants:
1. Le type de déplacement est const.
La raison pour laquelle nous avons le mot-clé const dans le langage est que nous voulons que le compilateur empêche toute modification d'un objet défini comme étant const. Prenant l'exemple du livre de Scott Meyers:
Qu'est-ce que cela signifie littéralement? Déplacez une chaîne const vers le membre de valeur - du moins, c'est ce que je comprends avant de lire l'explication.
Si le langage a l'intention de ne pas faire de mouvement ou de ne pas garantir que move est applicable lorsque std :: move () est appelé, alors il est littéralement trompeur lors de l'utilisation de word move.
Si le langage encourage les utilisateurs de std :: move à avoir une meilleure efficacité, il doit éviter les pièges comme celui-ci le plus tôt possible, en particulier pour ce type de contradiction littérale évidente.
Je conviens que les gens doivent être conscients que déplacer une constante est impossible, mais cette obligation ne doit pas impliquer que le compilateur peut se taire lorsqu'une contradiction évidente se produit.
2. L'objet n'a pas de constructeur de déplacement
Personnellement, je pense que c'est une histoire distincte de la préoccupation d'OP, comme l'a dit Chris Drew
la source
Je suis surpris que personne n'ait mentionné l'aspect de compatibilité descendante de cela. Je crois, a
std::move
été conçu à dessein pour faire cela en C ++ 11. Imaginez que vous travaillez avec une base de code héritée, qui repose fortement sur les bibliothèques C ++ 98, donc sans le repli sur l'affectation de copie, le déplacement casserait les choses.la source
Heureusement, vous pouvez utiliser le contrôle de clang-tidy pour trouver de tels problèmes: https://clang.llvm.org/extra/clang-tidy/checks/performance-move-const-arg.html
la source