243 votes

Avantages du passage par valeur et de std::move par rapport au passage par référence

J'apprends le C++ en ce moment et j'essaie d'éviter de prendre de mauvaises habitudes. D'après ce que j'ai compris, clang-tidy contient de nombreuses "meilleures pratiques" et j'essaie de m'y tenir le mieux possible (même si je ne comprends pas forcément pourquoi ils sont déjà considérés comme bons), mais je ne suis pas sûr de comprendre ce qui est recommandé ici.

J'ai utilisé cette classe du tutoriel :

class Creature
{
private:
    std::string m_name;

public:
    Creature(const std::string &name)
            :  m_name{name}
    {
    }
};

C'est pourquoi clang-tidy m'a suggéré de passer par une valeur au lieu d'une référence et d'utiliser std::move . Si c'est le cas, on me propose de faire name une référence (pour s'assurer qu'il n'est pas copié à chaque fois) et l'avertissement suivant std::move n'aura aucun effet car name est un const Je devrais donc le supprimer.

La seule façon de ne pas recevoir d'avertissement est de supprimer const tout à fait :

Creature(std::string name)
        :  m_name{std::move(name)}
{
}

Ce qui semble logique, puisque le seul avantage de const était d'éviter d'altérer la chaîne de caractères originale (ce qui n'est pas le cas puisque je suis passé par valeur). Mais j'ai lu CPlusPlus.com :

Notez toutefois que, dans la bibliothèque standard, le déplacement implique que l'objet déplacé est laissé dans un état valide mais non spécifié. Cela signifie qu'après une telle opération, la valeur de l'objet déplacé ne doit être détruite ou affectée d'une nouvelle valeur que si l'on y accède autrement, on obtient une valeur non spécifiée.

Imaginez maintenant ce code :

std::string nameString("Alex");
Creature c(nameString);

Parce que nameString est transmis par sa valeur, std::move n'invalidera que les name dans le constructeur et ne pas toucher à la chaîne de caractères originale. Mais quels sont les avantages de cette solution ? Il semble que le contenu ne soit copié qu'une seule fois de toute façon - si je passe par référence quand j'appelle m_name{name} Si je passe par la valeur lorsque je la passe (et qu'elle est ensuite déplacée). Je comprends que c'est mieux que de passer par valeur et de ne pas utiliser std::move (parce qu'il est copié deux fois).

Deux questions donc :

  1. Ai-je bien compris ce qui se passe ici ?
  2. Y a-t-il un avantage à utiliser std::move plutôt que de passer par référence et d'appeler simplement m_name{name} ?

7 votes

Avec passage par référence, Creature c("John"); fait une copie supplémentaire

2 votes

Ce lien pourrait être une lecture intéressante, il couvre le passage std::string_view et SSO.

5 votes

J'ai trouvé clang-tidy est un excellent moyen de m'obséder avec des micro-optimisations inutiles au détriment de la lisibilité. La question qu'il faut se poser ici, avant toute autre chose, est de savoir combien de fois nous en fait appeler le Creature constructeur.

-2voto

Danny Cohen Points 376

J'ai rencontré la même question et je l'ai résolue avec une classe d'habillage sympa.

https://stackoverflow.com/a/65357007/4037515

#pragma pack(push, 1)
template<class T>
class CopyOrMove{
public:
    CopyOrMove(T&&t):m_move(&t),m_isMove(true){}

    CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
    bool hasInstance()const{ return m_isMove; }
    const T& getConstReference() const {
        return *m_reference;
    } 
    T extract() && {
      if (hasInstance())
            return std::move(*m_move);
      else
            return *m_reference;
    }

    void fastExtract(T* out) && {
      if (hasInstance())
            *out = std::move(*m_move);
      else
            *out = *m_reference;
    }  
private:
    union
    {
        T* m_move;
        const T* m_reference;
    };
    bool m_isMove;
};
#pragma pack(pop)

Vous pouvez ensuite l'utiliser de cette manière :

Creature(CopyOrMove<std::string> name)
    :  m_name{std::move(name.extract())}
{
}

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