5 votes

Utilisation efficace de la sémantique de déplacement avec (N)RVO

Disons que je veux implémenter une fonction qui est censée traiter un objet et renvoyer un nouvel objet (éventuellement modifié). Je voudrais faire cela aussi efficacement que possible en C+11. L'environnement est le suivant :

class Object {
    /* Implementation of Object */
    Object & makeChanges();
};

Les alternatives qui me viennent à l'esprit sont :

// First alternative:
Object process1(Object arg) { return arg.makeChanges(); }
// Second alternative:
Object process2(Object const & arg) { return Object(arg).makeChanges(); }
Object process2(Object && arg) { return std::move(arg.makeChanges()); }
// Third alternative:
Object process3(Object const & arg) { 
    Object retObj = arg; retObj.makeChanges(); return retObj; 
}
Object process3(Object && arg) { std::move(return arg.makeChanges()); }

Note : Je voudrais utiliser une fonction d'enveloppement comme process() parce qu'il fera d'autres travaux et que j'aimerais que le code soit réutilisé autant que possible.

Mises à jour :

J'ai utilisé le makeChanges() avec la signature donnée parce que les objets que je traite fournissent des méthodes avec ce type de signature. Je suppose qu'ils ont utilisé cela pour le chaînage de méthodes. J'ai également corrigé les deux erreurs de syntaxe mentionnées. Merci de les avoir signalées. J'ai également ajouté une troisième alternative et je reposerai la question ci-dessous.

En les essayant avec clang [i.e. Object obj2 = process(obj); ] aboutit à ce qui suit :

La première option fait deux appels au constructeur de la copie ; un pour passer l'argument et un pour retourner. On pourrait plutôt dire return std::move(..) et avoir un appel au constructeur de copie et un appel au constructeur de déplacement. Je comprends que RVO ne peut pas se débarrasser de l'un de ces appels parce que nous avons affaire à un paramètre de fonction.

Dans la deuxième option, nous avons encore deux appels au constructeur de copie. Ici, nous faisons un appel explicite et un autre est fait lors du retour. Je m'attendais à ce que RVO intervienne et se débarrasse de ce dernier puisque l'objet que nous retournons est un objet différent de celui de l'argument. Cependant, cela ne s'est pas produit.

Dans la troisième option, nous n'avons qu'un seul appel au constructeur de copie et c'est l'appel explicite. Le (N)RVO élimine l'appel au constructeur de copie que nous ferions pour le retour.

Mes questions sont les suivantes :

  1. (répondu) Pourquoi le RVO intervient-il dans la dernière option et non dans la seconde ?
  2. Y a-t-il une meilleure façon de procéder ?
  3. Si nous avions passé un temporaire, les 2ème et 3ème options appelleraient un constructeur de déplacement lors du retour. Est-il possible d'éliminer cela en utilisant (N)RVO ?

Merci !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X