J'ai lu sur std::optional<:reference_wrapper>>
comme moyen de transmettre des références facultatives.
Cependant, je ne parviens pas à trouver de pratique exemple où je ferais cela, au lieu d'utiliser simplement un optionnel
.
Par exemple, supposons que j'écris une fonction qui doit prendre un
Je pourrais faire ceci:
void f(optional<vector<int>>& v) { // ... } int main() { f(make_optional<vector<int>>>(std::initializer_list{1, 2, 3, 4})); return 0; }
Mais pourquoi ne pas faire ça? p >
void f(optional<reference_wrapper<vector<int>>> v) { // ... } int main() { vector<int> v = {1, 2, 3, 4}; f(make_optional<reference_wrapper<vector<int>>>(std::ref(v)); return 0; }
Veuillez donner un exemple où optionnel
est préférable à optionnel
. Les différences sémantiques, et en particulier la manière dont elles peuvent être exploitées dans la pratique, ne sont pas claires pour moi.
3 Réponses :
std :: optional
est une référence à un objet optionnel qui peut posséder un objet T
. Vous pouvez muter un T
(s'il y en a un) ou vous pouvez effacer l'objet facultatif qui a été passé par référence, détruisant le T
contenu.
std::optional
est un objet facultatif qui peut posséder une référence à un T
, mais ce n'est pas le cas. t possède réellement le T
lui-même. Le T
vit à l'extérieur de l'objet std :: optional
. Vous pouvez muter le T
(si une référence est contenue) ou vous pouvez effacer l'objet optionnel, ce qui ne détruit pas le T
. Vous pouvez également faire pointer l'objet optionnel sur un T
différent, mais ce serait un peu inutile puisque l'appelant vous passe un optionnel par valeur.
Notez que nous avons déjà un type intégré à la langue qui signifie "référence facultative à un T
": T *
. Un pointeur brut et une référence facultative ont fondamentalement la même sémantique: soit vous n'obtenez rien, soit vous obtenez un handle vers un objet que vous ne possédez pas. Dans le C ++ moderne, un pointeur brut est le moyen d'exprimer une valeur facultative n'appartenant pas au récepteur.
Je ne peux pas penser à une seule raison pour laquelle j'utiliserais explicitement std::optional
au lieu de T *
.
Je vois - facultatif
force la propriété à vie de x
sur l'objet T
, c'est-à-dire si nous avons déjà un T
objet, nous devons le copier dans x
. facultatif
ne possède pas l'objet T
, car reference_wrapper
ne possède pas le T
, donc il peut être utilisé lorsque nous avons déjà un objet T
existant (mais dans ce cas, autant utiliser un simple T *
). Tout est clair, merci.
@AvivCohn Oui, c'est exactement ça!
Mais pourquoi ne pas faire ça?
Parce que votre code ne se compile pas. make_optional
renvoie une prvalue, et vous ne pouvez pas passer une prvalue à une fonction qui prend une référence non- const
lvalue.
C'est important car cela montre la différence fondamentale entre ces deux cas. Si vous avez déjà un T
ou une référence à un T
ailleurs, vous ne pouvez pas le transmettre à une fonction qui prend un facultatif
. Vous devrez copier le T
dans une variable optionnelle
, puis passer une référence à la variable optionnelle
à la fonction. < / p>
Vous ne pourriez pas modifier le T
du monde extérieur. Et c'est la différence: avec reference_wrapper
, c'est possible.
Ou si vous avez une fonction qui peut fonctionner avec ou sans un T
modifiable, vous pouvez simplement passer un T *
comme la plupart des gens le feraient.
Je vois - c'est une question de propriété. optionnel
possède la durée de vie de l'objet T
(et optionnel
est juste une référence à un optionnel < T>
, tandis que optional
possède la durée de vie du reference_wrapper
, qui n'est pas propriétaire la durée de vie de l'objet T
. Ainsi les différentes sémantiques. Merci!
Comme l'indiquent les autres réponses, les deux types ont des objectifs différents, car la référence est à des choses différentes (une référence à un Le résultat de ce code: optionnel
dans un cas et une référence à un vecteur code> dans l'autre). Plutôt que de répéter l'explication, voici un code avec lequel vous pouvez jouer pour voir les différences fonctionnelles.
Using a reference to the vector:
Original vector size: 1
Optional vector size: 1
Using a copy of the vector:
Original vector size: 0
Optional vector size: 5
#include <iostream>
#include <vector>
#include <functional>
#include <optional>
// For better readability:
using optional_reference_vector = std::optional<std::reference_wrapper<std::vector<int>>>;
using optional_vector = std::optional<std::vector<int>>;
void f(optional_reference_vector v) {
v->get().push_back(5);
}
void g(optional_vector & w) {
w->push_back(5);
}
int main() {
// Two identical vectors with which to work:
std::vector<int> v = {1, 2, 3, 4};
std::vector<int> w = {1, 2, 3, 4};
// Demonstrate an optional reference to a vector
// ---------------------------------------------
// Create a reference to `v` in `opt_v`.
// Changes to `opt_v` will be reflected in `v` (and vice versa).
optional_reference_vector opt_v {std::ref(v)};
v.clear();
// A copy of `opt_v` will be made in f(). Since we are copying a reference to
// a vector and not the vector itself, the vector in main() is changed by f().
f(opt_v);
// Both `v` and `opt_v` refer to the same vector, so the size is the same.
std::cout << "Using a reference to the vector:\n"
<< "Original vector size: " << v.size() << '\n'
<< "Optional vector size: " << opt_v->get().size() << "\n\n";
// Demonstrate a reference to an optional vector
// ---------------------------------------------
// Copy `w` into `opt_w`.
// Changes to `opt_w` have no effect on `w` (and vice versa).
optional_vector opt_w {w};
w.clear();
// A reference to `opt_w` will be used in g(), so `opt_w` is updated.
g(opt_w);
// There are two vectors that now have different sizes.
std::cout << "Using a copy of the vector:\n"
<< "Original vector size: " << w.size() << '\n'
<< "Optional vector size: " << opt_w->size() << '\n';
return 0;
}
Pensez aux valeurs de retour. Le
facultatif
serait une variable temporaire renvoyée.Quand êtes-vous dans une situation où vous choisissez entre ces deux? Ils signifient des choses différentes ... utilisez celui qui signifie la chose pertinente pour le problème.