수색…


비고

Variant는 원시 union 사용을 대체합니다. 그것은 유형 안전하고 어떤 유형인지 알고 있으며, 조심스럽게 그것을 구성하고 그 안에있는 객체를 파괴해야합니다.

그것은 거의 결코 비어 있지 않습니다. 내용을 대체하는 것이 구석에서 발생하고 안전하게 되돌릴 수없는 경우에만 비어있는 상태가됩니다.

이것은 std::tuple 과 다소 유사하고 std::optional 과 다소 유사합니다.

std::getstd::get_if 를 사용하는 것은 나쁜 생각입니다. 정답은 일반적으로 std::visit 입니다. 바로 std::visit 하여 모든 가능성을 처리 할 수 ​​있습니다. if constexprvisit 내에서 사용할 수 있다면 어떤 visit 이 더 효율적으로 반복되는지 런타임 검사 시퀀스를 수행하는 것이 아니라 동작을 분기해야합니다.

기본 표준 :: 변형 사용

이렇게하면 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 가있는 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 가지 타입이 있다면 :

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) 메소드가 없으므로 컴파일에 실패합니다.

위의 내용을 확장하면 free function print 가 감지되어 사용 될 수 있으며 아마도 print pseudo-method 내에서 if constexpr 을 사용 if constexpr 가능합니다.

std::variant 대신에 boost::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