Recherche…


Remarques

La variante remplace l’ union brute. Il est de type sécurisé et sait quel type il est, et il construit et détruit soigneusement les objets qu'il contient.

Il n'est presque jamais vide: ce n'est que dans les cas où le remplacement de son contenu se produit et qu'il est impossible de le rétablir en toute sécurité qu'il se trouve dans un état vide.

Il se comporte un peu comme un std::tuple , et un peu comme un std::optional .

Utiliser std::get et std::get_if est généralement une mauvaise idée. La bonne réponse est généralement std::visit , qui vous permet de traiter toutes les possibilités tout de suite. if constexpr peut être utilisé dans la visit si vous devez if constexpr branche de votre comportement, plutôt que d'effectuer une série de vérifications à l'exécution qui dupliquent ce que la visit fera plus efficacement.

Utilisation de base std :: variant

Cela crée une variante (une union balisée) qui peut stocker un int ou une string .

std::variant< int, std::string > var;

Nous pouvons en stocker un des deux types:

var = "hello"s;

Et nous pouvons accéder au contenu via std::visit :

// Prints "hello\n":
visit( [](auto&& e) {
  std::cout << e << '\n';
}, var );

en passant dans un objet polymorphe lambda ou une fonction similaire.

Si nous sommes certains de savoir de quel type il s'agit, nous pouvons l’obtenir:

auto str = std::get<std::string>(var);

mais cela se produira si nous nous trompons. get_if :

auto* str  = std::get_if<std::string>(&var);

renvoie nullptr si vous vous trompez.

Les variantes ne garantissent aucune allocation de mémoire dynamique (autre que celle allouée par leurs types contenus). Seul un des types d'une variante y est stocké et dans de rares cas (impliquant des exceptions lors de l'attribution et aucun moyen sûr de revenir en arrière), la variante peut devenir vide.

Les variantes vous permettent de stocker plusieurs types de valeurs dans une variable de manière sûre et efficace. Ce sont fondamentalement des union intelligents et sûrs.

Créer des pointeurs de pseudo-méthode

Ceci est un exemple avancé.

Vous pouvez utiliser une variante pour un effacement léger.

template<class F>
struct pseudo_method {
  F f;
  // enable C++17 class type deduction:
  pseudo_method( F&& fin ):f(std::move(fin)) {}

  // Koenig lookup operator->*, as this is a pseudo-method it is appropriate:
  template<class Variant> // maybe add SFINAE test that LHS is actually a variant.
  friend decltype(auto) operator->*( Variant&& var, pseudo_method const& method ) {
    // var->*method returns a lambda that perfect forwards a function call,
    // behaving like a method pointer basically:
    return [&](auto&&...args)->decltype(auto) {
      // use visit to get the type of the variant:
      return std::visit(
        [&](auto&& self)->decltype(auto) {
          // decltype(x)(x) is perfect forwarding in a lambda:
          return method.f( decltype(self)(self), decltype(args)(args)... );
        },
        std::forward<Var>(var)
      );
    };
  }
};

Cela crée un type qui surcharge l' operator->* avec un Variant sur le côté gauche.

// C++17 class type deduction to find template argument of `print` here.
// a pseudo-method lambda should take `self` as its first argument, then
// the rest of the arguments afterwards, and invoke the action:
pseudo_method print = [](auto&& self, auto&&...args)->decltype(auto) {
  return decltype(self)(self).print( decltype(args)(args)... );
};

Maintenant, si nous avons 2 types chacun avec une méthode d' print :

struct A {
  void print( std::ostream& os ) const {
    os << "A";
  }
};
struct B {
  void print( std::ostream& os ) const {
    os << "B";
  }
};

Notez qu'ils sont des types non liés. Nous pouvons:

std::variant<A,B> var = A{};

(var->*print)(std::cout);

et il enverra l'appel directement à A::print(std::cout) pour nous. Si on initialise la var avec B{} , elle sera envoyée à B::print(std::cout) .

Si nous avons créé un nouveau type C:

struct C {};

puis:

std::variant<A,B,C> var = A{};
(var->*print)(std::cout);

ne parviendra pas à compiler, car il n'y a pas de C.print(std::cout) .

L'extension de ce qui précède permettrait de détecter et d'utiliser une print fonction libre, éventuellement avec l'utilisation de if constexpr dans la pseudo-méthode d' print .

Exemple actuel utilisant boost::variant à la place de std::variant .

Construire un `std :: variant`

Cela ne couvre pas les allocateurs.

struct A {};
struct B { B()=default; B(B const&)=default; B(int){}; };
struct C { C()=delete; C(int) {}; C(C const&)=default; };
struct D { D( std::initializer_list<int> ) {}; D(D const&)=default; D()=default; };

std::variant<A,B> var_ab0; // contains a A()
std::variant<A,B> var_ab1 = 7; // contains a B(7)
std::variant<A,B> var_ab2 = var_ab1; // contains a B(7)
std::variant<A,B,C> var_abc0{ std::in_place_type<C>, 7 }; // contains a C(7)
std::variant<C> var_c0; // illegal, no default ctor for C
std::variant<A,D> var_ad0( std::in_place_type<D>, {1,3,3,4} ); // contains D{1,3,3,4}
std::variant<A,D> var_ad1( std::in_place_index<0> ); // contains A{}
std::variant<A,D> var_ad2( std::in_place_index<1>, {1,3,3,4} ); // contains D{1,3,3,4}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow