Zoeken…


Opmerkingen

Variant is een vervanging voor raw union gebruik. Het is typeveilig en weet welk type het is, en het construeert en vernietigt zorgvuldig de objecten erin wanneer het zou moeten.

Het is bijna nooit leeg: alleen in hoekgevallen waar het vervangen van de inhoud gooit en het niet veilig kan worden verwijderd, bevindt het zich in een lege staat.

Het gedraagt zich enigszins als een std::tuple en enigszins als een std::optional .

Het gebruik van std::get en std::get_if is meestal een slecht idee. Het juiste antwoord is meestal std::visit , waarmee u elke mogelijkheid daar kunt aanpakken. if constexpr binnen het visit kan worden gebruikt als u uw gedrag moet vertakken, in plaats van een reeks runtime-controles uit te voeren die dupliceren welk visit efficiënter zal doen.

Basic standaard :: variant gebruik

Dit creëert een variant (een gelabelde unie) waarin een int of een string kan worden opgeslagen.

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

We kunnen er een van beide typen in opslaan:

var = "hello"s;

En we hebben toegang tot de inhoud via std::visit :

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

door een polymorfe lambda of een soortgelijk functieobject in te voeren.

Als we zeker weten dat we weten welk type het is, kunnen we het krijgen:

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

maar dit zal gooien als we het fout hebben. get_if :

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

geeft nullptr terug als u verkeerd raadt.

Varianten garanderen geen dynamische geheugentoewijzing (behalve welke wordt toegewezen door hun ingesloten typen). Slechts een van de typen in een variant wordt daar opgeslagen en in zeldzame gevallen (met uitzonderingen tijdens het toewijzen en geen veilige manier om terug te gaan) kan de variant leeg raken.

Met varianten kunt u meerdere waardetypen veilig en efficiënt opslaan in één variabele. Het zijn eigenlijk slimme, typeveilige union .

Maak pseudomethode-aanwijzers

Dit is een geavanceerd voorbeeld.

U kunt een variant gebruiken voor het wissen van lichte soorten.

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

dit creëert een type dat de operator->* overbelast operator->* met een Variant aan de linkerkant.

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

Als we nu twee typen hebben met elk een print :

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

merk op dat het niet-gerelateerde types zijn. We kunnen:

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

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

en het verzendt de oproep rechtstreeks naar A::print(std::cout) voor ons. Als we in plaats daarvan de var initialiseren met B{} , wordt deze verzonden naar B::print(std::cout) .

Als we een nieuw type C hebben gemaakt:

struct C {};

vervolgens:

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

zal niet compileren, omdat er geen C.print(std::cout) methode is.

Uitbreiding van het bovenstaande zou toelaten dat vrije functie- print worden gedetecteerd en gebruikt, mogelijk met gebruik van if constexpr binnen de print pseudo-methode.

live voorbeeld gebruikt momenteel boost::variant in plaats van std::variant .

Een `std :: variant` construeren

Dit heeft geen betrekking op toewijzers.

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow