Поиск…


замечания

Вариант является заменой для использования в сыром union . Он безопасен по типу и знает, какой он тип, и он тщательно конструирует и уничтожает объекты внутри него, когда это необходимо.

Он почти никогда не бывает пустым: только в угловых случаях, когда замена его содержимого бросается, и он не может безопасно отступить, в конечном итоге он находится в пустом состоянии.

Он ведет себя как std::tuple и несколько похож на std::optional .

Использование std::get и std::get_if - обычно плохая идея. Правильный ответ, как правило, std::visit , который позволяет вам иметь дело с любой возможностью прямо там. if constexpr можно использовать в ходе visit если вам нужно разветвить ваше поведение, вместо того, чтобы выполнять последовательность проверок времени выполнения, которые дублируют то, что visit будет делать более эффективно.

Основное использование std :: variant

Это создает вариант (тегированный союз), который может хранить либо int либо string .

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

Мы можем сохранить в нем один из них:

var = "hello"s;

И мы можем получить доступ к содержимому через std::visit :

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

путем прохождения в полиморфном лямбда или подобном функциональном объекте.

Если мы уверены, что знаем, что это такое, мы можем это получить:

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

но это будет бросать, если мы ошибаемся. get_if :

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

возвращает nullptr если вы ошибаетесь.

Варианты не гарантируют динамического распределения памяти (кроме того, что выделяется их содержащимися типами). Там хранится только один из типов в варианте, и в редких случаях (с исключениями при назначении и без безопасного пути для возврата) вариант может стать пустым.

Варианты позволяют хранить несколько типов значений в одной переменной безопасно и эффективно. Они в основном умные, безопасные по типу union .

Создание указателей псевдо-методов

Это расширенный пример.

Вы можете использовать вариант для стирания легкого веса.

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

это создает тип, который перегружает operator->* с помощью Variant с левой стороны.

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

Теперь, если у нас есть 2 типа с методом print :

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

обратите внимание, что они не связаны между собой. Мы можем:

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

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

и он отправит вызов непосредственно на A::print(std::cout) для нас. Если мы вместо этого инициализировали var с B{} , он отправил бы B::print(std::cout) .

Если мы создали новый тип C:

struct C {};

затем:

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

не будет компилироваться, потому что не существует C.print(std::cout) .

Расширение вышеизложенного позволит обнаружить и использовать функцию бесплатной print s, возможно, с использованием if constexpr в псевдо-методе print .

живой пример в настоящее время использует boost::variant вместо std::variant .

Построение `std :: variant`

Это не распространяется на распределители.

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow