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
facultatifserait 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.