1
votes

optionnel > vs optionnel & - exemples pratiques?

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 vector par référence.

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.


2 commentaires

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.


3 Réponses :


2
votes

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


2 commentaires

Je vois - facultatif & x = ... 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!



1
votes

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.


1 commentaires

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!



0
votes

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

Le résultat de ce code:

#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;
}


0 commentaires