Suche…


Bemerkungen

Variante ist ein Ersatz für rohe union Einsatz. Es ist typensicher und weiß, um welchen Typ es sich handelt, und es konstruiert und zerstört die Objekte in dem Objekt sorgfältig, wenn es sollte.

Es ist fast nie leer: Nur in Eckfällen, in denen das Ersetzen des Inhalts geworfen wird und er nicht sicher zurückkehren kann, endet er in einem leeren Zustand.

Es verhält sich etwas wie ein std::tuple und etwas wie ein std::optional .

Die Verwendung von std::get und std::get_if ist normalerweise eine schlechte Idee. Die richtige Antwort ist in der Regel std::visit , so dass Sie direkt mit jeder Möglichkeit umgehen können. if constexpr innerhalb des visit werden kann, wenn Sie Ihr Verhalten verzweigen müssen, anstatt eine Laufzeitprüfung if constexpr , wird überprüft, ob der visit effizienter ist.

Grundsätzliche Verwendung von std :: variant

Dadurch wird eine Variante (eine markierte Union) erstellt, in der entweder ein int oder ein string gespeichert werden kann.

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

Wir können einen von beiden Typen darin speichern:

var = "hello"s;

Und wir können auf die Inhalte über std::visit zugreifen:

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

durch Einleiten eines polymorphen Lambda oder eines ähnlichen Funktionsobjekts.

Wenn wir sicher sind, um welchen Typ es sich handelt, können wir ihn bekommen:

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

aber das wird werfen, wenn wir es falsch verstehen. get_if :

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

nullptr wenn Sie falsch raten.

Varianten garantieren keine dynamische Speicherzuweisung (anders als bei den enthaltenen Typen). Dort ist nur einer der Typen gespeichert, und in seltenen Fällen (mit Ausnahmen beim Zuweisen und ohne sichere Rücknahme) kann die Variante leer werden.

Mit Varianten können Sie mehrere Wertetypen sicher und effizient in einer Variablen speichern. Sie sind im Grunde intelligente, typsichere union .

Erstellen Sie Pseudo-Methodenzeiger

Dies ist ein fortgeschrittenes Beispiel.

Sie können die Variante für das Löschen von Leichtgewichten verwenden.

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

Dadurch wird ein Typ erstellt, der operator->* mit einer Variant auf der linken Seite überladen.

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

Wenn wir nun 2 Typen mit einer print :

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

Beachten Sie, dass es sich nicht um Typen handelt. Wir können:

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

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

Der Anruf wird direkt an A::print(std::cout) für uns A::print(std::cout) . Wenn wir das var stattdessen mit B{} initialisieren, wird es an B::print(std::cout) .

Wenn wir einen neuen Typ C erstellt haben:

struct C {};

dann:

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

wird nicht kompiliert, da es keine C.print(std::cout) -Methode gibt.

Die oben erstreckt würde freie Funktion erlauben print s erkannt und verwendet werden, gegebenenfalls unter Verwendung von , if constexpr innerhalb des print pseudo-Methode.

Live-Beispiel, das momentan boost::variant anstelle von std::variant .

Konstruktion einer `std :: variant`

Dies gilt nicht für die Zuteiler.

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow