std::variant<Types...>::operator=
cppreference.net에서 가져옴
constexpr variant& operator=( const variant& rhs );
|
(1) | (C++17부터) |
constexpr variant& operator=( variant&& rhs ) noexcept(/* see below */);
|
(2) | (C++17부터) |
template< class T >
variant& operator=( T&& t ) noexcept(/* see below */);
|
(3) | (C++17부터) (C++20부터 constexpr) |
기존 variant 객체에 새 값을 할당합니다.
1) 복사 할당:
*this와rhs가 모두 예외로 인해 값이 없으면 아무것도 하지 않습니다.- 그렇지 않고,
rhs가 값이 없지만*this가 값이 있으면,*this에 포함된 값을 파괴하고 값이 없도록 만듭니다. - 그렇지 않고,
rhs가*this와 같은 대안을 보유하면,rhs에 포함된 값을*this에 포함된 값에 할당합니다. 예외가 발생하면*this는 값이 없게 되지 않습니다: 값은 대안의 복사 할당에 대한 예외 안전성 보장에 따라 달라집니다. - 그렇지 않고,
rhs가 보유한 대안이 nothrow 복사 생성 가능하거나 not nothrow 이동 생성 가능하지 않으면 (std::is_nothrow_copy_constructible 및 std::is_nothrow_move_constructible에 의해 각각 결정됨),this->emplace<rhs.index()>(*std::get_if<rhs.index()>(std::addressof(rhs)))와 동일합니다.*this는valueless_by_exception내부 복사 생성 중 예외가 발생하면emplace이 될 수 있습니다. - 그렇지 않으면
this->operator=(variant(rhs))와 동일합니다.
이 오버로드는
std::is_copy_constructible_v<T_i>과 std::is_copy_assignable_v<T_i>가 모두 true 안의 모든 T_i에 대해 Types...인 경우에만 삭제된 것으로 정의됩니다. 이 오버로드는 std::is_trivially_copy_constructible_v<T_i>,std::is_trivially_copy_assignable_v<T_i> 및 std::is_trivially_destructible_v<T_i>가 모두 true 안의 모든 T_i에 대해 Types...인 경우 사소합니다.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 안의 모든 T_i에 대해 Types...인 경우에만 오버로드 해결에 참여합니다. 이 오버로드는 std::is_trivially_move_constructible_v<T_i>, std::is_trivially_move_assignable_v<T_i> 및 std::is_trivially_destructible_v<T_i>가 모두 true 안의 모든 T_i에 대해 Types...인 경우 사소합니다.3) 변환 할당.
- 다음을 제외하고, 동시에
T_j로부터의 모든F(std::forward<T>(t))에 대해 가상 함수F(T_i)의 오버로드가 있다면 표현식T_i에 대해 오버로드 해결에 의해 선택될 대안 타입Types...를 결정합니다:
- 선언
F(T_i)이 어떤 발명된 변수T_i x[] = { std::forward<T>(t) };에 대해 유효한 경우에만 오버로드x가 고려됩니다;
- 선언
*this가 이미T_j를 보유하고 있으면,std::forward<T>(t)를*this에 포함된 값에 할당합니다. 예외가 발생하면*this는 값이 없게 되지 않습니다: 값은 호출된 할당의 예외 안전성 보장에 따라 달라집니다.- 그렇지 않고,
std::is_nothrow_constructible_v<T_j, T> || !std::is_nothrow_move_constructible_v<T_j>가true이면,this->emplace<j>(std::forward<T>(t))와 동일합니다.*this는valueless_by_exception내부 초기화 중 예외가 발생하면emplace이 될 수 있습니다. - 그렇지 않으면
this->emplace<j>(T_j(std::forward<T>(t)))와 동일합니다.
이 오버로드는 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"; // OK
std::variant<std::string, std::string> v2;
v2 = "abc"; // Error
std::variant <std::string, bool> v3;
v3 = "abc"; // OK, chooses string; bool is not a candidate
std::variant<float, long, double> v4; // holds float
v4 = 0; // OK, holds long; float and double are not candidates
매개변수
| 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 > ) && ... ) )
std:: is_nothrow_move_assignable_v < Types > ) && ... ) )
3)
noexcept
명세:
noexcept
(
std::
is_nothrow_assignable_v
<
T_j
&
, T
>
&&
std:: is_nothrow_constructible_v < T_j, T > )
std:: is_nothrow_constructible_v < T_j, T > )
참고 사항
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__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) |