Szukaj…


Uwagi

Wariant jest zamiennikiem surowego zastosowania union . Jest bezpieczny dla typu i wie, jaki to jest typ, i ostrożnie konstruuje i niszczy znajdujące się w nim obiekty, kiedy powinien.

Prawie nigdy nie jest pusta: tylko w narożnych przypadkach, w których zamiana zawartości rzuca się i nie może się bezpiecznie wycofać, kończy się w stanie pustym.

Zachowuje się trochę jak std::tuple , a trochę jak std::optional .

Używanie std::get i std::get_if jest zwykle złym pomysłem. Prawidłowa odpowiedź to zwykle std::visit , która pozwala poradzić sobie z każdą dostępną tam możliwością. if constexpr może być użyty w trakcie visit jeśli potrzebujesz rozgałęzić swoje zachowanie, zamiast wykonywać sekwencję kontroli środowiska wykonawczego, która powiela to, co visit zrobi bardziej efektywnie.

Podstawowe użycie std :: wariant

Tworzy to wariant (oznaczony związek), który może przechowywać int lub string .

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

Możemy przechowywać w nim jeden z tych typów:

var = "hello"s;

I możemy uzyskać dostęp do zawartości poprzez std::visit :

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

przez przekazanie polimorficznej lambdy lub podobnego obiektu funkcji.

Jeśli jesteśmy pewni, że wiemy, jaki to typ, możemy go uzyskać:

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

ale to rzuci, jeśli źle to zrobimy. get_if :

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

zwraca nullptr jeśli zgadniesz źle.

Warianty nie gwarantują dynamicznej alokacji pamięci (innej niż alokacja według zawartych w nich typów). Przechowywany jest tam tylko jeden typ w wariancie, aw rzadkich przypadkach (z wyjątkami podczas przypisywania i brakiem bezpiecznego sposobu wycofania się) wariant może stać się pusty.

Warianty umożliwiają bezpieczne i wydajne przechowywanie wielu typów wartości w jednej zmiennej. Są to w zasadzie sprytne, bezpieczne dla typu union .

Utwórz wskaźniki pseudo-metody

To jest zaawansowany przykład.

Możesz użyć wariantu do skasowania typu lekkiego.

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

tworzy to typ, który przeciąża operator->* Variant po lewej stronie.

// 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)... );
};

Teraz, jeśli mamy 2 typy, każdy z metodą print :

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

zauważ, że są to typy niepowiązane. Możemy:

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

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

i wyśle nam połączenie bezpośrednio do A::print(std::cout) . Jeśli zamiast tego zainicjalizujemy var pomocą B{} , wysyłamy go do B::print(std::cout) .

Jeśli stworzyliśmy nowy typ C:

struct C {};

następnie:

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

nie uda się skompilować, ponieważ nie ma C.print(std::cout) .

Rozszerzanie powyżej, pozwalają na swobodny funkcji print S być wykrywane i stosowane ewentualnie z zastosowaniem if constexpr w print pseudo sposobem.

przykład na żywo obecnie korzystający z boost::variant zamiast std::variant .

Konstruowanie `std :: wariant`

Nie dotyczy to osób przydzielających.

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow