Sök…


Anmärkningar

Variant är en ersättning för rå union användning. Den är typsäker och vet vilken typ den är, och den konstruerar och förstör försiktigt föremålen i den när den borde.

Det är nästan aldrig tomt: endast i hörnfall där utbyte av innehåll kastas och det inte kan ryggas ut säkert hamnar det i tomt tillstånd.

Det uppför sig något som en std::tuple , och något som en std::optional .

Att använda std::get och std::get_if är vanligtvis en dålig idé. Rätt svar är vanligtvis std::visit , vilket gör att du kan hantera alla möjligheter där. if constexpr kan användas under visit om du behöver förgrena ditt beteende, snarare än att göra en sekvens av runtime-kontroller som duplicerar vad visit kommer att göra mer effektivt.

Grundläggande std :: variantanvändning

Detta skapar en variant (en taggad union) som kan lagra antingen en int eller en string .

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

Vi kan lagra en av båda typerna i den:

var = "hello"s;

Och vi kan komma åt innehållet via std::visit :

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

genom att passera in en polymorfisk lambda eller liknande funktionsobjekt.

Om vi är säkra på att vi vet vilken typ det är, kan vi få det:

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

men detta kommer att kasta om vi gör fel. get_if :

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

returnerar nullptr om du gissar fel.

Varianter garanterar ingen dynamisk minnesallokering (annat än som tilldelas av deras innehåll). Endast en av typerna i en variant lagras där, och i sällsynta fall (med undantag vid tilldelning och inget säkert sätt att backa ut) kan varianten bli tom.

Med varianter kan du lagra flera värdetyper i en variabel säkert och effektivt. De är i grunden smarta, typsäkra union .

Skapa pseudo-metodpekare

Detta är ett avancerat exempel.

Du kan använda variant för radering av lätt vikt.

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

detta skapar en typ som överbelaster operator->* med en Variant på vänster sida.

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

Om vi nu har två typer med en print :

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

Observera att de inte är relaterade typer. Vi kan:

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

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

och det kommer att skicka samtalet direkt till A::print(std::cout) för oss. Om vi istället initialiserade var med B{} , skulle det skickas till B::print(std::cout) .

Om vi skapade en ny typ C:

struct C {};

sedan:

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

kommer inte att kompilera, eftersom det inte finns någon C.print(std::cout) -metod.

Förlängning av ovan skulle tillåta fri funktion print s som skall detekteras och användas, eventuellt med användning av if constexpr inom print pseudo-metoden.

levande exempel som för närvarande använder boost::variant istället för std::variant .

Konstruera en `std :: variant`

Detta täcker inte tilldelare.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow