サーチ…


備考

バリアントは、元のunion使用に代わるものです。これは型保証され、どの型かを知っており、必要な時にその中のオブジェクトを慎重に構築し破棄します。

それはほとんど決して空ではありません:そのコンテンツを置き換えて安全に戻ってくることができないコーナーケースでのみ、それは空の状態に終わるかどうかです。

これはstd::tupleように動作し、 std::optionalと多少似ていstd::optional

std::getstd::get_ifは、普通は悪い考えです。正しい答えは通常はstd::visitであり、そこではあらゆる可能性に対処できます。あなたの行動を分岐させる必要visit場合、 visitより効率的に行うvisitに実行時検査のシーケンスを実行するのではなく、 if constexprvisit内で使用できるかどうかを確認します。

基本的な::バリアントの使用

これにより、 intまたはstring格納できるバリアント(タグ付き共用体)が作成され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返します。

バリアントは、ダイナミックメモリ割り当てを保証しません(ただし、含まれている型によって割り当てられる以外は)。バリアント内の1つの型だけがそこに格納されており、まれに(割り当て中に例外が発生し、バックアウトする安全な方法がない)、バリアントが空になることがあります。

バリアントを使用すると、複数の値タイプを1つの変数に安全かつ効率的に格納できます。彼らは基本的にスマートな、型安全な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)
      );
    };
  }
};

これは、左側にVariantを持つoperator->*をオーバーロードする型を作成します。

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

今では、それぞれにprintメソッドを持つ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)に直接呼び出しをA::print(std::cout)ます。代わりにB{}varを初期化すると、 B::print(std::cout)ディスパッチされます。

新しいタイプCを作成した場合:

struct C {};

次に:

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

C.print(std::cout)メソッドがないため、コンパイルに失敗します。

上記を拡張することで、フリー関数のprintを検出して使用することができます。おそらく、 print疑似メソッド内でif constexprを使用することで可能です。

ライブの例では、現在使用して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