std::variant<Types...>::operator=
來自 cppreference.com
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 可能會變為
valueless_by_exception
,如果在emplace
內部的複製構造過程中丟擲異常。 - 否則,等價於 this->operator=(variant(rhs))。
除非
Types...
中所有 T_i
的 std::is_copy_constructible_v<T_i> 和 std::is_copy_assignable_v<T_i> 都為 true,否則此過載被定義為刪除。如果 Types...
中所有 T_i
的 std::is_trivially_copy_constructible_v<T_i>、std::is_trivially_copy_assignable_v<T_i> 和 std::is_trivially_destructible_v<T_i> 都為 true,則此過載是平凡的。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
。
僅當
Types...
中所有 T_i
的 std::is_move_constructible_v<T_i> 和 std::is_move_assignable_v<T_i> 都為 true 時,此過載才參與過載決議。如果 Types...
中所有 T_i
的 std::is_trivially_move_constructible_v<T_i>、std::is_trivially_move_assignable_v<T_i> 和 std::is_trivially_destructible_v<T_i> 都為 true,則此過載是平凡的。3) 轉換賦值。
- 確定如果對於
Types...
中每個T_i
都存在一個虛構函式 F(T_i) 的過載,表示式 F(std::forward<T>(t)) 將透過過載決議選擇的備選項型別T_j
,但以下情況除外:
- 僅當宣告 T_i x[] = { std::forward<T>(t) }; 對於某個虛構變數
x
有效時,才考慮過載 F(T_i);
- 僅當宣告 T_i x[] = { std::forward<T>(t) }; 對於某個虛構變數
- 如果 *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 | - | 另一個 variant |
t | - | 一個可轉換為 variant 某個備選項的值 |
[編輯] 返回值
*this
[編輯] 異常
1) 可能丟擲任何由任何備選項的賦值和複製/移動初始化丟擲的異常。
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++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
LWG 3024 | C++17 | 如果任何成員型別不可複製, 複製賦值運算子不參與過載決議, |
而是定義為刪除。 |
LWG 3585 | C++17 | 轉換賦值有時會意外地格式錯誤, 因為沒有可用的移動賦值。 |
使其格式正確 |
P0602R4 | C++17 | 複製/移動賦值可能不是平凡的, 即使底層操作是平凡的。 |
要求傳播平凡性 |
P0608R3 | C++17 | 轉換賦值盲目地組建過載集, 導致意外轉換。 |
窄化和布林轉換 不考慮 |
P2231R1 | C++20 | 轉換賦值 (3) 不是 constexpr 而在 C++20 中所需操作可以是 constexpr 的。 |
設為 constexpr |
[編輯] 參閱
在 variant 中原地構造一個值(公共成員函式) |