Buscar..


Observaciones

La variante es un reemplazo para el uso de la union cruda. Es de tipo seguro y sabe de qué tipo es, y construye y destruye cuidadosamente los objetos dentro de él cuando debería.

Casi nunca está vacío: solo en los casos de esquina donde se reemplaza el contenido y no se puede retroceder de manera segura, se queda en un estado vacío.

Se comporta algo así como un std::tuple , y algo así como un std::optional .

Usar std::get y std::get_if suele ser una mala idea. La respuesta correcta suele ser std::visit , que le permite lidiar con todas las posibilidades allí mismo. if constexpr se puede usar if constexpr dentro de la visit si necesita ramificar su comportamiento, en lugar de hacer una secuencia de verificaciones de tiempo de ejecución que dupliquen lo que la visit hará de manera más eficiente.

Basic std :: uso variante

Esto crea una variante (una unión etiquetada) que puede almacenar un int o una string .

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

Podemos almacenar uno de los dos tipos en él:

var = "hello"s;

Y podemos acceder a los contenidos a través de std::visit :

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

Pasando en un objeto lambda polimórfico o función similar.

Si estamos seguros de saber qué tipo es, podemos obtenerlo:

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

Pero esto tirará si nos equivocamos. get_if :

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

devuelve nullptr si nullptr mal.

Las variantes no garantizan una asignación de memoria dinámica (aparte de la que se asigna por sus tipos contenidos). Solo uno de los tipos en una variante se almacena allí, y en casos raros (que involucran excepciones al asignar y no hay una forma segura de retroceder), la variante puede quedar vacía.

Las variantes le permiten almacenar múltiples tipos de valores en una variable de manera segura y eficiente. Básicamente son union inteligentes y seguros.

Crear punteros pseudo-método

Este es un ejemplo avanzado.

Puede utilizar la variante para borrar el tipo de peso ligero.

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

esto crea un tipo que sobrecarga al operator->* con una Variant en el lado izquierdo.

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

Ahora si tenemos 2 tipos cada uno con un método de print :

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

Tenga en cuenta que son tipos no relacionados. Podemos:

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

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

y enviará la llamada directamente a A::print(std::cout) para nosotros. Si en su lugar inicializamos la var con B{} , se enviaría a B::print(std::cout) .

Si creamos un nuevo tipo C:

struct C {};

entonces:

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

no se compilará porque no hay ningún C.print(std::cout) .

La extensión de lo anterior permitiría que se detecten y utilicen las print función libre, posiblemente con el uso de if constexpr dentro del pseudo-método de print .

Ejemplo vivo actualmente usando boost::variant en lugar de std::variant .

Construyendo un `std :: variant`

Esto no cubre los asignadores.

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow