34 votes

Pourquoi le retour d’un std:: optionnel parfois déplacer et parfois copier?

Voir l’exemple ci-dessous de retourner une option de `` - une classe mobile / copyable.

Pourquoi cause-t-il une copie et un mouvement ?

Voici l’échantillon de colirus connexe : http://coliru.stacked-crooked.com/a/6bf853750b38d110

Un autre cas (grâce au commentaire de @Slava):

28voto

xskxzr Points 5160

Parce que le renvoi d'un nom d'un objet automatique de la durée de stockage est traitée comme au retour d'une rvalue de l'objet. Remarque cela ne fonctionne que si l'expression dans l'instruction return est une (peut-être mis entre parenthèses, n'incluant pas les accolades) nom, comme return u; ou return (u);, alors return {u}; fonctionne comme d'habitude, c'est à dire le constructeur de copie est appelé.

La partie liée dans la norme [classe.copie.élision]/3:

Dans la suite de la copie de l'initialisation de contextes, une opération de déplacement peut être utilisé à la place d'une opération de copie:

  • Si l'expression dans une instruction de retour ([stmt.retour]) est un (éventuellement entre parenthèses) id-expression désignant un objet avec stockage automatique de la durée déclarée dans le corps ou le paramètre-déclaration de la clause d'enfermer les plus secrets de la fonction ou de la lambda-expression, ou
  • ...

résolution de surcharge pour sélectionner le constructeur de copie est effectuée que si l'objet ont été désignés par une rvalue.

5voto

sandthorn Points 988

C'est une sorte d' arc-boutée-init-liste. [dcl.init.liste]/1.3

Pour être encore plus précis, c'est un " expr-ou-calé-init-liste [dcl.init]/1

d'une instruction return " [stmt.retour]/2

Une instruction de retour avec un autre opérande doit être utilisé uniquement dans un fonction dont le type de retour n'est pas cv vide; l'instruction de retour initialise le glvalue résultat ou prvalue objet de résultat de l' (explicite ou implicite) d'appel de fonction par copier-initialisation de la l'opérande.

À partir de ce point, permettez-moi de qoutes xskxzr réponse qui mentionnent [classe.copie.élision]/3

Dans la suite de la copie de l'initialisation de contextes, une opération de déplacement peut être utilisé à la place d'une opération de copie:

  • Si l'expression dans une instruction de retour ([stmt.retour]) est un (éventuellement entre parenthèses) id-expression désignant un objet avec stockage automatique de la durée déclarée dans le corps ou le paramètre-déclaration de la clause d'enfermer les plus secrets de la fonction ou de la lambda-expression, ou

Normale de la parole humaine, la raison pour laquelle la copie est appelée à la place de bouger car l'embrassa-init-liste d'appels u qui est arrivé à être lvalue.

Ainsi, vous pouvez veux savoir si moisa-init-liste d'appels u qui est rvalue ...

return {std::move(u)};

Eh bien, u est déplacé vers un nouveau rvalue d' UserName et copie élision travaille juste après.

Donc, cela prend un coup comme dans

return u;

godbolt.org/g/b6stLr

wandbox.org/permlink/7u1cPc0TG9gqToZD

#include <iostream>
#include <optional>

struct UserName
{
  int x;
  UserName() : x(0) {};
  UserName(const UserName& other) : x(other.x) { std::cout << "copy " << x << "\n"; };
  UserName(UserName&& other)      : x(other.x) { std::cout << "move "  << x << "\n"; };
};

std::optional<UserName> CreateUser()
{
  UserName u;
  return u;   // this one moves UserName
}

std::optional<UserName> CreateUser_listinit()
{
  UserName u;
  auto whatever{u};
  return whatever;
}

std::optional<UserName> CreateUser_listinit_with_copy_elision()
{
  UserName u;
  return {u};
}

std::optional<UserName> CreateUser_move_listinit_with_copy_elision()
{
  UserName u;
  return {std::move(u)};
}

int main()
{
  std::cout << "CreateUser() :\n";
  [[maybe_unused]] auto d = CreateUser();

  std::cout << "\nCreateUser_listinit() :\n";
  [[maybe_unused]] auto e = CreateUser_listinit();

  std::cout << "\nCreateUser_listinit_with_copy_elision() :\n";
  [[maybe_unused]] auto f = CreateUser_listinit_with_copy_elision();

  std::cout << "\nCreateUser_move_listinit_with_copy_elision() :\n";
  [[maybe_unused]] auto g = CreateUser_move_listinit_with_copy_elision();
}

imprimer

CreateUser() :
move 0

CreateUser_listinit() :
copy 0
move 0

CreateUser_listinit_with_copy_elision() :
copy 0

CreateUser_move_listinit_with_copy_elision() :
move 0

1voto

Zang MingJie Points 1839
<blockquote> <p>retour de <em>l’arg1, de l’arg2, de la ...</em></p> </blockquote> <p>est <strong><a href="https://en.cppreference.com/w/cpp/language/list_initialization" rel="nofollow noreferrer">la copy-list-initialization</a></strong>. l’objet (retour) est initialisé à partir de la liste d’initialisation par copy-initialization pour la copy-list-initialization</p>

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