Ceci est une question académique. Le type std::optional
a une méthode T && value () &&
. J'ai les définitions suivantes:
std::optional<A> optA; optA = A(1337); f(std::move(optA).value()); // OPTION 1 f(std::move(optA.value())); // OPTION 2 std::cout << optA.has_value() << std::endl;
Et le programme suivant:
class A { ... }; void f(A &&a);
Y a-t-il une différence significative entre OPTION 1 et OPTION 2? Pour l'OPTION 1, aurai-je 1
, 0
ou non spécifié comme sortie? D'après mes tests, has_value () est resté vrai dans les deux cas.
Y a-t-il une situation possible où value () &&
fait une différence par rapport à std :: move code> et
value()
?
4 Réponses :
Il n'y a aucune différence entre std :: move (optA) .value ()
et std :: move (optA.value ())
. value ()
renvoie simplement une valeur de glissement faisant référence à la valeur contenue, et soit vous pouvez avoir value ()
renvoyer une lvalue qui est ensuite convertie en une xvalue par std :: move
, ou vous pouvez appeler std :: move
d'abord et demander à value ()
de vous donner immédiatement une valeur x (en réalité, la conversion de lvalue to xvalue se produira quelque part dans la méthode value ()
elle-même). Les surcharges qualifiées ref ne sont évidemment pas très utiles dans ce cas simple, mais peuvent être utiles lorsque l'option optionnelle a été passée par la référence de transfert O && o
, et que vous voulez std :: forward
pour "faire ce qu'il faut".
f(std::move(optA.value())); // OPTION 2
Votre première phrase n'a pas de sens. std :: move
ne bouge pas. std :: move (x)
ne déplace pas x . Dire que l'OP "a déménagé" optA
n'a pas de sens (ou, plus précisément, est faux). Vous clarifiez plus tard, mais vous commencez par quelque chose qui n'est pas vrai. Confondre le problème avec de fausses déclarations n'est pas un excellent plan de réponse.
@ Yakk-AdamNevraumont: et vous me blâmez? ils l'ont appelé move
après tout :) J'ai essayé de paraphraser la réponse, et j'ai échoué
Y a-t-il une situation possible où value () && fait une différence par rapport à std :: move et value ()?
Tenez compte de ce qui suit:
struct C {string s;}; C func2() {...} void h(string s); h(func2().s);
f
Le paramètreopt
sera initialisé par move. Eh bien techniquement, il sera initialisé directement par la prvalue, mais pré-C ++ 17 signifie qu'il est initialisé par déplacement. Cette initialisation peut être éludée, mais si ce n'est pas le cas, elle se fait via move. Toujours.Mais qu'en est-il du paramètre de
g
? Que devrait-il arriver? Eh bien, considérez ce que cela ferait:optional<A> func() {...} void f(optional<A> opt) {...} void g(A a) {...} f(func()); g(func().value());Le paramètre de
h
est initialisé par move . Pourquoi? Parce que si vous accédez à un sous-objet membre d'une prvalue, l'expression résultante est une xvalue, et est donc éligible pour le mouvement sans utiliser explicitementstd::move
.Le
&&
constructeur defacultatif
garantit que lavaleur
fonctionne de la même manière. Si vous l'appelez sur une prvalue temporaire, alors il retournera une valeur x, qui peut être déplacée sans un appel explicitestd :: move
. Donc dans le cas d'origine, le paramètre deg
est initialisé par move, exactement comme il l'aurait fait s'il accédait à un sous-objet membre d'une valeur pr.
Je suppose que les deux versions sont identiques, cependant, la réponse de Nicol Bolas semble intéressante.
En supposant qu'elles aboutissent toutes les deux à une rvalue et que le code n'a pas d'importance, nous n'avons aucune raison d'ignorer la lisibilité .
std::move(optA.value())
Cela suggère de déplacer la variable et de compter sur le rédacteur de la classe pour fournir les bonnes surcharges. S'il manque, vous risquez de le rater.
Certains linters reconnaissent également cela comme une variable à ne plus toucher, ce qui vous empêche de l'utiliser après le déplacement.
std::move(optA).value()
Ceci convertit cependant la valeur de retour en une rvalue. Je considère surtout ce code comme un bogue. Soit la fonction retourne par valeur (ou référence const) et nous avons écrit beaucoup de code. Sinon, la fonction renvoie une lvalue et vous avez potentiellement ruiné l'état interne de la variable.
Avec cela, je recommande de déplacer systématiquement la variable et de déclencher le déplacement d'un appel de fonction comme une odeur de code. (Même pour std :: facultatif, là où cela ne devrait pas avoir d'importance)
Re:
has_value ()
est restétrue
: cela dépend def ()
. Modifiez-vous réellementa
dansf ()
?@lorro Cela ne dépend en fait pas de
f
- notez quef
obtient unA
, il n'obtient pasoptA code>.
has_value ()
ne changerait que sif
accédait d'une manière ou d'une autre àoptA
directement (en tant que global?)@Barry: après avoir quitté un optionnel lui-même, il est dans un état valide mais indéterminé (à ma connaissance). Votre implémentation peut rester telle quelle. Ou il pourrait le définir sur nullopt. Ou toute valeur globale - hautement irréaliste mais possible, en particulier. avec des types d'agrégats. Au contraire, lorsque vous accédez à value (), il est garanti que l’option est conservée telle quelle, car c’est une référence non déplaçable.
@lorro Rien ici ne sort de l'option.
@Barry: std :: move (optA) .value () choisira valeur () && sur valeur ().
@lorro qui déplace une valeur stockée dans l'option. Cela ne déplace pas l'option. C'est semblable à
std :: vector foo (100); auto x = std :: move (foo [0]);
vsauto y = std :: move (foo);
- on déplace quelque chose stocké dans le vecteur, l'autre se déplace le vecteur. Toutes les opérations déplacent quelque chose stocké dans l’option, aucune ne déplace l’option.@ Yakk-AdamNevraumont: Pouvez-vous s'il vous plaît m'indiquer où la norme dit que
std :: optional :: value () &&
(notez le&&
) garantit que < code> * this n'est pas vide après l'appel (c'est-à-dire qu'il ne retourne pas le drapeau pour décrire s'il est vide - notez qu'il n'est pas nécessaire qu'il soit le même que pour détruire ou non l'objet dans le espace de rangement)? Ma connaissance actuelle est quevalue () &&
garantit que leoptionnel
est dans un état valide mais indéterminé, tandis quevalue ()
garantit qu'il reste non vide et l'objet contenu est dans un état valide mais indéterminé.@lorro 23.6.3.5 [facultatif.observer] / 16? Comme dans, l'endroit où
value () &&
est défini? "Équivalent àreturn bool (* this)? Std :: move (* val): throw bad_optional_access ()
". L'opérateur*
ne désactive pas lefacultatif
, et le déplacement des données dans l'option facultative peut ne pas changer l'état d'engagement de l'option. Je pourrais trouver le bit où «la bibliothèque standard n'est pas autorisée à vous déranger», mais il existe une règle générale selon laquelle lorsque vous interagissez avec l'objet A, vous ne pouvez pas jouer avec l'objet B à moins que la norme ne le dise.@ Yakk-AdamNevraumont: Thx, TIL quelque chose. Donc dans ce cas, ce qui précède ne peut pas être fait par le compilateur et donc l'option optionnelle gardera sa valeur (si elle en avait une). Merci!