Namespaces
Variants

std::variant<Types...>:: operator=

From cppreference.net
Utilities library
constexpr variant & operator = ( const variant & rhs ) ;
(1) (C++17부터)
constexpr variant & operator = ( variant && rhs ) noexcept ( /* 아래 참조 */ ) ;
(2) (C++17부터)
template < class T >
variant & operator = ( T && t ) noexcept ( /* 아래 참조 */ ) ;
(3) (C++17부터)
(C++20부터 constexpr)

기존 variant 객체에 새로운 값을 할당합니다.

1) 복사 할당:
  • 만약 * this rhs 모두 예외로 인해 값이 없는 상태라면, 아무 작업도 수행하지 않습니다.
  • 그렇지 않고 rhs 가 값이 없는 상태이지만 * this 는 그렇지 않은 경우, * this 에 포함된 값을 파괴하고 값이 없는 상태로 만듭니다.
  • 그렇지 않고 rhs * this 와 동일한 대체 타입을 보유하고 있는 경우, rhs 에 포함된 값을 * this 에 포함된 값에 할당합니다. 예외가 발생하면 * this 는 값이 없는 상태가 되지 않습니다: 값은 대체 타입의 복사 할당 예외 안전성 보장에 따라 달라집니다.
  • 그렇지 않고 rhs 가 보유한 대체 타입이 nothrow 복사 생성 가능하거나 아니면 nothrow 이동 생성 가능하지 않은 경우(각각 std::is_nothrow_copy_constructible std::is_nothrow_move_constructible 로 결정됨), this - > emplace < rhs. index ( ) > ( * std:: get_if < rhs. index ( ) > ( std:: addressof ( rhs ) ) ) 와 동일합니다. * this emplace 내부의 복사 생성에서 예외가 발생하면 valueless_by_exception 이 될 수 있습니다.
  • 그렇지 않으면 this - > operator = ( variant ( rhs ) ) 와 동일합니다.
이 오버로드는 std:: is_copy_constructible_v < T_i > std:: is_copy_assignable_v < T_i > 가 모두 true 인 경우가 아니면 삭제된 것으로 정의됩니다. 이 오버로드는 std:: is_trivially_copy_constructible_v < T_i > , std:: is_trivially_copy_assignable_v < T_i > 그리고 std:: is_trivially_destructible_v < T_i > 가 모두 true 인 경우 trivial합니다.
2) 이동 할당:
  • 만약 * this rhs 모두 예외에 의해 값이 없는 상태라면, 아무 작업도 수행하지 않습니다.
  • 그렇지 않고 rhs 가 값이 없는 상태이지만 * this 는 그렇지 않은 경우, * this 에 포함된 값을 파괴하고 값이 없는 상태로 만듭니다.
  • 그렇지 않고 rhs * this 와 동일한 대체 타입을 보유하고 있는 경우, std :: move ( * std:: get_if < j > ( std:: addressof ( rhs ) ) ) * this 에 포함된 값에 할당합니다. 여기서 j index() 입니다. 예외가 발생하면 * this 는 값이 없는 상태가 되지 않습니다: 값은 대체 타입의 이동 할당 예외 안전성 보장에 따라 달라집니다.
  • 그 외의 경우 ( rhs * this 가 서로 다른 대체 타입을 보유하는 경우), this - > emplace < rhs. index ( ) > ( std :: move ( * std:: get_if < rhs. index ( ) > ( std:: addressof ( rhs ) ) ) ) 와 동등합니다. T_i 의 이동 생성자에서 예외가 발생하면 * this valueless_by_exception 가 됩니다.
이 오버로드는 std:: is_move_constructible_v < T_i > std:: is_move_assignable_v < T_i > 가 모두 true 인 경우에만 오버로드 해결에 참여합니다. 이 오버로드는 std:: is_trivially_move_constructible_v < T_i > , std:: is_trivially_move_assignable_v < T_i > , 그리고 std:: is_trivially_destructible_v < T_i > 가 모두 true 인 경우 trivial합니다.
3) Converting assignment.
  • 오버로드 해결이 다음 표현식에 대해 선택할 대체 타입 T_j 를 결정합니다: F ( std:: forward < T > ( t ) ) 단, 다음과 같은 가정 하에: 모든 T_i 에 대해 가상 함수 F ( T_i ) 의 오버로드가 Types... 내의 각 타입에 대해 동시에 스코프 내에 존재한다고 가정합니다. 단, 다음 예외 사항이 적용됩니다:
  • 오버로드 F ( T_i ) 는 선언문 T_i x [ ] = { std:: forward < T > ( t ) } ; 가 어떤 가상 변수 x 에 대해 유효할 때만 고려됩니다;

이 오버로드는 다음 조건에서만 오버로드 해결에 참여합니다: std:: decay_t < T > (C++20 이전) std:: remove_cvref_t < T > (C++20 이후) variant 과 같은 타입이 아니고, std:: is_assignable_v < T_j & , T > true 이며, std:: is_constructible_v < T_j, T > true 이고, 표현식 F ( std:: forward < T > ( t ) ) (F는 위에서 언급된 가상 함수들의 집합)이 형성 가능할 때입니다.

std::variant<std::string> v1;
v1 = "abc"; // 정상
std::variant<std::string, std::string> v2;
v2 = "abc"; // 오류
std::variant <std::string, bool> v3;
v3 = "abc"; // 정상, string 선택; bool은 후보가 아님
std::variant<float, long, double> v4; // float 보유
v4 = 0; // 정상, long 보유; float과 double은 후보가 아님

목차

매개변수

rhs - another variant
t - variant의 대안 중 하나로 변환 가능한 값

반환값

* this

예외

1) 대안(alternative)의 할당 및 복사/이동 초기화에서 발생하는 모든 예외를 던질 수 있습니다.
2)
noexcept 명세:
noexcept ( ( ( std:: is_nothrow_move_constructible_v < Types > &&
std:: is_nothrow_move_assignable_v < Types > ) && ... ) )
3)

참고 사항

기능 테스트 매크로 표준 기능
__cpp_lib_variant 202106L (C++20)
(DR)
완전한 constexpr std::variant ( 3 )

예제

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
std::ostream& operator<<(std::ostream& os, std::variant<int, std::string> const& va)
{
    os << ": { ";
    std::visit([&](auto&& arg)
    {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
            os << arg;
        else if constexpr (std::is_same_v<T, std::string>)
            os << std::quoted(arg);
    }, va);
    return os << " };\n";
}
int main()
{
    std::variant<int, std::string> a{2017}, b{"CppCon"};
    std::cout << "a" << a << "b" << b << '\n';
    std::cout << "(1) operator=( const variant& rhs )\n";
    a = b;
    std::cout << "a" << a << "b" << b << '\n';
    std::cout << "(2) operator=( variant&& rhs )\n";
    a = std::move(b);
    std::cout << "a" << a << "b" << b << '\n';
    std::cout << "(3) operator=( T&& t ), where T is int\n";
    a = 2019;
    std::cout << "a" << a << '\n';
    std::cout << "(3) operator=( T&& t ), where T is std::string\n";
    std::string s{"CppNow"};
    std::cout << "s: " << std::quoted(s) << '\n';
    a = std::move(s);
    std::cout << "a" << a << "s: " << std::quoted(s) << '\n';
}

가능한 출력:

a: { 2017 };
b: { "CppCon" };
(1) operator=( const variant& rhs )
a: { "CppCon" };
b: { "CppCon" };
(2) operator=( variant&& rhs )
a: { "CppCon" };
b: { "" };
(3) operator=( T&& t ), where T is int
a: { 2019 };
(3) operator=( T&& t ), where T is std::string
s: "CppNow"
a: { "CppNow" };
s: ""

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
LWG 3024 C++17 복사 할당 연산자가 오버로드 해결에 참여하지 않음
멤버 타입 중 복사 불가능한 타입이 있는 경우
대신 삭제된 것으로 정의됨
LWG 3585 C++17 변환 할당이 때때로 예기치 않게 ill-formed였음
사용 가능한 이동 할당이 없었기 때문
well-formed로 변경됨
P0602R4 C++17 복사/이동 할당이 trivial하지 않을 수 있음
기본 연산들이 trivial한 경우에도
triviality 전파 요구됨
P0608R3 C++17 변환 할당이 무분별하게 오버로드 집합을 구성하여
의도하지 않은 변환으로 이어짐
축소 변환과 불린 변환
고려되지 않음
P2231R1 C++20 변환 할당 ( 3 ) constexpr 이 아니었음
필요한 연산들이 C++20에서 constexpr 이 될 수 있음에도
constexpr 으로 변경됨

참고 항목

variant 내에 값을 직접 생성
(public member function)