- 這里開發(fā)一個類似于std::variant的類模板Variant,Variant提供了比union更好的類型安全捶索,其行為如下
#include "variant.hpp"
#include <iostream>
#include <string>
int main()
{
Variant<int, double, std::string> v(42);
if (v.is<int>()) std::cout << v.get<int>(); // 42
v = "hi";
std::cout << v.get<std::string>(); // hi
}
存儲
- Variant的首要設計是管理活動值的存儲,不同類型可能有不同的大小和對齊要考慮问潭,一個簡單(盡管低效)的存儲機制是直接使用一個tuple。此外還需要存儲一個discriminator作為tuple的索引沙峻,比如discriminator為0時睦授,
get<0>(storage)
訪問活動值
template<typename... Types>
class Variant {
public:
Tuple<Types...> storage;
unsigned char discriminator;
};
- 但這種做法導致Variant需要所有類型大小總和的空間两芳,即使某個時刻只有一個活動值摔寨。更好的做法是重疊每個可能類型的空間,這可以通過遞歸拆分variant為首部和尾部來實現(xiàn)
template<typename... Types>
union VariantStorage;
template<typename Head, typename... Tail>
union VariantStorage<Head, Tail...> {
Head head;
VariantStorage<Tail...> tail;
};
template<>
union VariantStorage<> {};
- 這里的union保證足夠的大小和對齊來存儲參數(shù)列表的任意類型怖辆,但這個union本身很難使用是复,因為大多用來實現(xiàn)Variant的技術將使用繼承删顶,而這對union是不允許的。取代做法是用最底層的字符數(shù)組來存儲淑廊,這個數(shù)組足夠大到存儲任何類型(通過一個計算最大類型的type traits)逗余,并對任何類型有合適的對齊(通過alignas指定符)
#include <new> // for std::launder()
template<typename... Types>
class VariantStorage {
using LargestT = LargestType<Typelist<Types...>>;
alignas(Types...) unsigned char buffer[sizeof(LargestT)];
unsigned char discriminator = 0;
public:
unsigned char getDiscriminator() const { return discriminator; }
void setDiscriminator(unsigned char d) { discriminator = d; }
void* getRawBuffer() { return buffer; }
const void* getRawBuffer() const { return buffer; }
template<typename T>
T* getBufferAs() { return std::launder(reinterpret_cast<T*>(buffer)); }
template<typename T>
const T* getBufferAs() const
{
return std::launder(reinterpret_cast<const T*>(buffer));
}
};
設計
- 接著設計Variant類型本身。和Tuple一樣季惩,使用繼承對每個Types中的類型提供行為录粱。不同于Tuple的是,這些基類沒有存儲画拾。相反啥繁,每個基類使用CRTP通過最派生的類型訪問共享的Variant存儲。定義如下VariantChoice類模板,它提供活動值為類型T時字符數(shù)組上所需的核心操作
template<typename T, typename... Types>
class VariantChoice {
using Derived = Variant<Types...>;
Derived& getDerived() { return *static_cast<Derived*>(this); }
const Derived& getDerived() const
{
return *static_cast<const Derived*>(this);
}
protected:
constexpr static unsigned Discriminator =
FindIndexOfT<Typelist<Types...>, T>::value + 1;
public:
VariantChoice() {}
// see variantchoiceinit.hpp
VariantChoice(const T& value);
VariantChoice(T&& value);
bool destroy(); // see variantchoicedestroy.hpp
// see variantchoiceassign.hpp
Derived& operator=(const T& value);
Derived& operator=(T&& value);
};
- 元函數(shù)FindIndexOfT用來找出Types中類型T的位置青抛,實現(xiàn)如下
template<typename List, typename T, unsigned N = 0,
bool Empty = IsEmpty<List>::value>
struct FindIndexOfT;
// recursive case:
template<typename List, typename T, unsigned N>
struct FindIndexOfT<List, T, N, false>
: public IfThenElse<std::is_same<Front<List>, T>::value,
std::integral_constant<unsigned, N>,
FindIndexOfT<PopFront<List>, T, N+1>>
{};
// basis case:
template<typename List, typename T, unsigned N>
struct FindIndexOfT<List, T, N, true>
{};
- Variant繼承于VariantStorage和VariantChoice
template<typename... Types>
class Variant : private VariantStorage<Types...>,
private VariantChoice<Types, Types...>...
{
template<typename T, typename... OtherTypes>
friend class VariantChoice; // enable CRTP
...
};
Variant<int, double, std::string>
VariantChoice<int, int, double, std::string>,
VariantChoice<double, int, double, std::string>,
VariantChoice<std::string, int, double, std::string>
- 這三個基類對應的discriminator值將為1旗闽、2、3蜜另。當Variant的存儲成員discriminator匹配一個特定基類的VariantChoice::Discriminator時适室,基類負責管理活動值
- discriminator值為0被保留用于Variant不包含值的情況,這是一種奇態(tài)(odd state)举瑰,只有在分配期間拋出異常時才能觀察到
- Variant完整定義如下
template<typename... Types>
class Variant : private VariantStorage<Types...>,
private VariantChoice<Types, Types...>...
{
template<typename T, typename... OtherTypes>
friend class VariantChoice;
public:
template<typename T> bool is() const; // see variantis.hpp
// see variantget.hpp
template<typename T> T& get() &;
template<typename T> const T& get() const&;
template<typename T> T&& get() &&;
// see variantvisit.hpp:
template<typename R = ComputedResultType, typename Visitor>
VisitResult<R, Visitor, Types&...> visit(Visitor&& vis) &;
template<typename R = ComputedResultType, typename Visitor>
VisitResult<R, Visitor, const Types&...> visit(Visitor&& vis) const&;
template<typename R = ComputedResultType, typename Visitor>
VisitResult<R, Visitor, Types&&...> visit(Visitor&& vis) &&;
using VariantChoice<Types, Types...>::VariantChoice...;
Variant(); // see variantdefaultctor.hpp
Variant(const Variant& source); // see variantcopyctor.hpp
Variant(Variant&& source); // see variantmovector.hpp
template<typename... SourceTypes>
Variant(const Variant<SourceTypes...>& source); // see variantcopyctortmpl.hpp
template<typename... SourceTypes>
Variant(Variant<SourceTypes...>&& source); // see variantmovectortmpl.hpp
using VariantChoice<Types, Types...>::operator=...;
Variant& operator= (const Variant& source); // see variantcopyassign.hpp
Variant& operator= (Variant&& source); // see variantmoveassign.hpp
template<typename... SourceTypes>
Variant& operator= (const Variant<SourceTypes...>& source); // see variantcopyassigntmpl.hpp
template<typename... SourceTypes>
Variant& operator= (Variant<SourceTypes...>&& source); // see variantmoveassigntmpl.hpp
bool empty() const; // see variantempty.hpp
~Variant() { destroy(); }
void destroy(); // see variantdestroy.hpp
};
查詢
- is()成員函數(shù)定義如下捣辆,用于確定Variant當前是否存儲一個T類型值
// variant/variantis.hpp
template<typename... Types>
template<typename T>
bool Variant<Types...>::is() const
{
return this->getDiscriminator() ==
VariantChoice<T, Types...>::Discriminator;
}
// v.is<int>()將確定v的活動值是否為int
- 如果查找的類型未在列表中在找到,F(xiàn)indIndexOfT將不會包含一個value成員嘶居,于是VariantChoice基類將實例化失敗罪帖,從而造成is()的編譯錯誤,這樣就可以防止用戶請求無法存儲的類型
提取
- get()成員函數(shù)通過接受一個類型邮屁,提取一個該類型存儲值的引用整袁,并且僅當Variant的活動值是該類型時才有效。當Variant未保存一個值(即discriminator為0)時佑吝,get()拋出一個EmptyVariant異常
// variant/variantget.hpp
#include <exception>
class EmptyVariant : public std::exception {};
template<typename... Types>
template<typename T>
T& Variant<Types...>::get() &
{
if (empty()) throw EmptyVariant();
assert(is<T>());
return *this->template getBufferAs<T>();
}
初始化
- 從一個類型的值初始化一個Variant坐昙,通過一個VariantChoice構造函數(shù)實現(xiàn),這個構造函數(shù)接受一個T類型的值
// variant/variantchoiceinit.hpp
template<typename T, typename... Types>
VariantChoice<T, Types...>::VariantChoice(const T& value)
{
// getDerived()使用CRTP來操作派生類
new(getDerived().getRawBuffer()) T(value); // 用placement new將value置入buffer
// 設置派生類的discriminator為基類的Discriminator來表示存儲的類型
getDerived().setDiscriminator(Discriminator);
}
template<typename T, typename... Types>
VariantChoice<T, Types...>::VariantChoice(T&& value)
{
new(getDerived().getRawBuffer()) T(std::move(value));
getDerived().setDiscriminator(Discriminator);
}
- 最終的目標是能從一個任意類型的值初始化Variant芋忿,甚至可以隱式轉換
Variant<int, double, string> v("hello"); // 隱式轉換為string
- 為此使用using聲明把VariantChoice的構造函數(shù)引入到Variant中
using VariantChoice<Types, Types...>::VariantChoice...;
- 這個using聲明為Types中的每個類型生成一個Variant的構造函數(shù)
// Variant<int, double, string>的構造函數(shù)實際是
Variant(const int&);
Variant(int&&);
Variant(const double&);
Variant(double&&);
Variant(const string&);
Variant(string&&);
析構
- 當Variant初始化時炸客,一個值構造到它的buffer中,destroy處理那個值的析構
// variant/variantchoicedestroy.hpp
template<typename T, typename... Types>
bool VariantChoice<T, Types...>::destroy()
{
if (getDerived().getDiscriminator() == Discriminator)
{
// 如果匹配則調(diào)用placement delete
getDerived().template getBufferAs<T>()->~T();
return true;
}
return false;
}
- 只有當discriminator匹配時戈钢,VariantChoice::destroy()操作是有用的痹仙。但通常希望不考慮當前活動的類型,直接銷毀存儲在Variant中的值殉了,因此Variant::destroy()將調(diào)用基類中所有的VariantChoice::destroy()
// variant/variantdestroy.hpp
template<typename... Types>
void Variant<Types...>::destroy()
{
// 調(diào)用所有的VariantChoice::destroy()开仰,至多一個成功
bool results[] = { VariantChoice<Types, Types...>::destroy()...};
this->setDiscriminator(0); // 表示不再存儲一個值,Variant為空
}
- 數(shù)組results的作用是提供一個能使用初始化列表的上下文,C++17中可以使用折疊表達式來消除對數(shù)組results的需要
// variant/variantdestroy17.hpp
template<typename... Types>
void Variant<Types...>::destroy()
{
(VariantChoice<Types, Types...>::destroy(), ...);
this->setDiscriminator(0);
}
賦值
- 對于同類型直接賦值众弓,對于不同類型則需要析構現(xiàn)有值恩溅,再用placement new重新初始化新類型的值
// variant/variantchoiceassign.hpp
template<typename T, typename... Types>
auto VariantChoice<T, Types...>::operator=(const T& value) -> Derived&
{
if (getDerived().getDiscriminator() == Discriminator) // 同類型的值
{
*getDerived().template getBufferAs<T>() = value;
}
else // 不同類型的值
{
getDerived().destroy(); // 用Variant::destroy()析構現(xiàn)有值
new(getDerived().getRawBuffer()) T(value); // 用placement new初始化T類型新值
getDerived().setDiscriminator(Discriminator);
}
return getDerived();
}
template<typename T, typename... Types>
auto VariantChoice<T, Types...>::operator=(T&& value) -> Derived&
{
if (getDerived().getDiscriminator() == Discriminator) // 同類型的值
{
*getDerived().template getBufferAs<T>() = std::move(value);
}
else // 不同類型的值
{
getDerived().destroy();
new(getDerived().getRawBuffer()) T(std::move(value));
getDerived().setDiscriminator(Discriminator);
}
return getDerived();
}
- 和初始化一樣,可以在Variant中使用using聲明來繼承賦值操作
using VariantChoice<Types, Types...>::operator=...;
- 對于不同類型谓娃,賦值有兩步脚乡,先析構再初始化,這將有三個需要考慮的問題:自賦值滨达、異常奶稠、std::launder
- 自賦值發(fā)生情況如下。如果使用兩步賦值捡遍,源值將在拷貝前會被析構窒典,導致內(nèi)存崩潰。但好在自賦值意味著discriminator匹配稽莉,所以會使用同類型賦值瀑志,因此不會出問題
v = v.get<T>()
- 如果現(xiàn)有值的銷毀已經(jīng)完成,但是新值的初始化拋出異常污秆,Variant::destroy()將discriminator值重置為0劈猪,表示Variant未存儲值
// variant/variantexception.cpp
class CopiedNonCopyable : public std::exception {};
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&)
{
throw CopiedNonCopyable();
}
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(const NonCopyable&)
{
throw CopiedNonCopyable();
}
NonCopyable& operator=(NonCopyable&&) = default;
};
int main()
{
Variant<int, NonCopyable> v(42);
try
{
NonCopyable nc;
v = nc;
}
catch (CopiedNonCopyable)
{
std::cout << "Copy assignment of NonCopyable failed." << '\n';
if (!v.is<int>() && !v.is<NonCopyable>())
{
std::cout << "Variant has no value.";
}
}
}
// 程序輸出為
Copy assignment of NonCopyable failed.
Variant has no value.
- 訪問一個沒有值的variant將拋出EmptyVariant異常,以允許程序從這個異常條件修復
- empty()成員函數(shù)用于檢查Variant是否為空
// variant/variantempty.hpp
template<typename... Types>
bool Variant<Types...>::empty() const
{
return this->getDiscriminator() == 0;
}
- 第三個問題是標準化委員會在C++ 17標準化過程結束時才意識到的一個微妙問題良拼。編譯器通常希望產(chǎn)生高性能代碼战得,而提高生成代碼性能的主要機制是避免從內(nèi)存到寄存器的重復的數(shù)據(jù)復制。為此編譯器必須做一些假設庸推,其中一種假設是某些類型的數(shù)據(jù)在其生命周期內(nèi)是不可變的常侦,這包括const數(shù)據(jù)、引用(可以初始化贬媒,但之后不修改)以及存儲在多態(tài)對象中的一些bookkeeping data(用于調(diào)度虛擬函數(shù)聋亡、定位虛擬基類、以及處理typeid和dynamic_cast操作符)
- 上面兩步分配過程的問題是际乘,它以一種編譯器可能無法識別的方式悄悄地結束一個對象的生命周期坡倔,并在同一位置開始另一個對象的生命周期。因此脖含,編譯器可能假定它從Variant對象的先前狀態(tài)獲取的值仍然有效罪塔,而實際上placement new的初始化會使其無效,這就導致編譯具有不可變數(shù)據(jù)成員的Variant時偶爾會產(chǎn)生無效結果养葵。這種bug通常很難追蹤(一部分原因是它們很少發(fā)生征堪,另一部分原因是它們在源代碼中并不真正可見)
- C++17中,這個問題的解決方案是使用std::launder关拒,它返回實參所表示地址的對象的指針佃蚜,使得編譯器不再假設原有值有效咳榜,從而起到修復的效果
- 還有一些方法可以做得更好,比如額外添加一個指向buffer的指針成員爽锥,并在每次使用placement new賦值一個新值時獲得已清洗的地址,但這些方法會使代碼變得復雜畔柔,難以維護
訪問者(Visitor)
- 檢查一個Variant中的所有可能類型會造成一個冗長的if語句鏈
if (v.is<int>())
{
std::cout << v.get<int>();
}
else if (v.is<double>())
{
std::cout << v.get<double>();
}
else
{
std::cout << v.get<string>();
}
template<typename V, typename Head, typename... Tail>
void printImpl(const V& v)
{
if (v.template is<Head>())
{
std::cout << v.template get<Head>();
}
else if constexpr (sizeof...(Tail) > 0)
{
printImpl<V, Tail...>(v);
}
}
template<typename... Types>
void print(const Variant<Types...>& v)
{
printImpl<Variant<Types...>, Types...>(v);
}
int main()
{
Variant<int, short, float, double> v(3.14);
print(v);
}
- 但對于一個相對簡單的操作來說氯夷,這是一個相當大的代碼量。為了簡化這點靶擦,可以用visit()來擴展Variant腮考,它接受一個仿函數(shù)或者lambda,接受的參數(shù)就相當于訪問者的角色
v.visit([] (const auto& value) { std::cout << value; });
- variantVisitImpl()遍歷Variant的類型玄捕,檢查活動值是否具有給定類型踩蔚,然后在找到適當類型時進行操作
template<typename R, typename V, typename Visitor,
typename Head, typename... Tail>
R variantVisitImpl(V&& variant, Visitor&& vis, Typelist<Head, Tail...>) {
if (variant.template is<Head>())
{
return static_cast<R>(
std::forward<Visitor>(vis)(
std::forward<V>(variant).template get<Head>()));
}
else if constexpr (sizeof...(Tail) > 0)
{
return variantVisitImpl<R>(std::forward<V>(variant),
std::forward<Visitor>(vis),
Typelist<Tail...>());
}
else
{
throw EmptyVariant();
}
}
- visit()的實現(xiàn)直接委托給variantVisitImpl()即可,傳遞Variant本身枚粘,轉發(fā)訪問者馅闽,并提供完整的類型列表。三種實現(xiàn)分別在this對象作為Variant&馍迄、const Variant&或Variant&&傳遞時被調(diào)用福也,類成員函數(shù)后加引用限定符的用法可參考《Effective Modern C++》條款12
// variant/variantvisit.hpp
template<typename... Types>
template<typename R, typename Visitor>
VisitResult<R, Visitor, Types&...>
Variant<Types...>::visit(Visitor&& vis)& {
using Result = VisitResult<R, Visitor, Types&...>;
return variantVisitImpl<Result>(*this, std::forward<Visitor>(vis),
Typelist<Types...>());
}
template<typename... Types>
template<typename R, typename Visitor>
VisitResult<R, Visitor, const Types&...>
Variant<Types...>::visit(Visitor&& vis) const& {
using Result = VisitResult<R, Visitor, const Types &...>;
return variantVisitImpl<Result>(*this, std::forward<Visitor>(vis),
Typelist<Types...>());
}
template<typename... Types>
template<typename R, typename Visitor>
VisitResult<R, Visitor, Types&&...>
Variant<Types...>::visit(Visitor&& vis) && {
using Result = VisitResult<R, Visitor, Types&&...>;
return variantVisitImpl<Result>(std::move(*this),
std::forward<Visitor>(vis),
Typelist<Types...>());
}
- 這里還需要實現(xiàn)VisitResult元函數(shù)。visit()的結果類型仍不能確定攀圈,比如接受如下lambda暴凑,其返回類型取決于輸入的參數(shù)類型
[] (const auto& value) { return value + 1; }
- visit()設置一個模板參數(shù)R,其默認實參為ComputedResultType
template<typename R = ComputedResultType, typename Visitor>
VisitResult<R, Visitor, Types&...> visit(Visitor&& vis) &;
- 現(xiàn)在希望顯式指定返回結果的類型時赘来,將R設為返回類型
// visit的返回類型是Variant<int, double>中的類型
v.visit<Variant<int, double>>([] (const auto& value) { return value + 1; });
- 不顯式指定時现喳,將使用R的默認實參ComputedResultType(一個只需聲明無需實現(xiàn)的類,只是用作標簽)
class ComputedResultType;
- 元函數(shù)VisitResult的實現(xiàn)如下
// 顯式指定返回類型的情形
template<typename R, typename Visitor, typename... ElementTypes>
class VisitResultT {
public:
using Type = R;
};
// 對使用默認實參ComputedResultType的情形特化一個版本
template<typename Visitor, typename… ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes…> {
... // 實現(xiàn)見后
}
template<typename R, typename Visitor, typename... ElementTypes>
using VisitResult = typename VisitResultT<R, Visitor, ElementTypes...>::Type;
- 對于通用類型(common type)犬辰,C++已經(jīng)有了一個合理的概念:在三元表達式
b ? x : y
中嗦篱,結果類型是x和y之間的通用類型,例如x為int幌缝,y為double默色,則通用類型為double。由此實現(xiàn)CommonType如下
using std::declval;
template<typename T, typename U>
class CommonTypeT {
public:
using Type = decltype(true? declval<T>() : declval<U>());
};
template<typename T, typename U>
using CommonType = typename CommonTypeT<T, U>::Type;
- 特化的VisitResultT得到對Variant的每個類型調(diào)用訪問者時生成的結果類型的通用類型狮腿,實現(xiàn)如下
// 對T類型值調(diào)用訪問者時生成的結果類型
template<typename Visitor, typename T>
using VisitElementResult = decltype(declval<Visitor>()(declval<T>()));
// 對每個元素類型調(diào)用訪問者時的通用結果類型
template<typename Visitor, typename... ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes...> {
using ResultTypes = // 每個元素調(diào)用訪問者時生成的結果類型
Typelist<VisitElementResult<Visitor, ElementTypes>...>;
public:
using Type = // 所有結果類型的通用類型
Accumulate<PopFront<ResultTypes>, CommonTypeT, Front<ResultTypes>>;
};
- 標準庫提供了std::common_type腿宰,它組合了CommonTypeT和Accumulate以對任意數(shù)量類型生成通用類型,因此特化的VisitResultT可以更簡單地實現(xiàn)如下
template<typename Visitor, typename... ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes...> {
public:
using Type =
std::common_type_t<VisitElementResult<Visitor, ElementTypes>...>;
};
- 下例說明了visit()接受一個訪問者時的返回類型
Variant<int, short, double, float> v(3.14);
auto result = v.visit([] (const auto& value) { return value + 1; });
std::cout << typeid(result).name(); // double
默認構造
- Variant應該允許默認構造缘厢,否則每次使用都要指定初始值乱投。對于默認構造的方式挂谍,可能會想到設置discriminator為0來表示沒有存儲值,但這樣的空Variant不能被訪問或找到任何要提取的值,并且將空Variant的異常狀態(tài)提升到了通用狀態(tài)
- 一個避免引入空Variant的方法是初始化類型列表中首個類型的一個值
// variant/variantdefaultctor.hpp
template<typename... Types>
Variant<Types...>::Variant()
{
*this = Front<Typelist<Types...>>();
}
Variant<int, double> v;
if (v.is<int>()) std::cout << v.get<int>() <<'\n'; // 0(int類型)
Variant<double, int> v2;
if (v2.is<double>()) std::cout << v2.get<double>(); // 0(double類型)
拷貝和移動構造
- 要拷貝一個Variant需要確定它當前存儲的類型,將該值拷貝構造到字符數(shù)組中略荡,并設置discriminator,這兩點在VariantChoice的拷貝賦值運算符中已提供,通過對要拷貝的Variant使用visit()即可簡潔緊湊地實現(xiàn)目的
// variant/variantcopyctor.hpp
template<typename... Types>
Variant<Types...>::Variant(const Variant& source)
{
if (!source.empty())
{
source.visit([&] (const auto& value) {
*this = value; // 從VariantChoice繼承的拷貝賦值運算符
});
}
}
// variant/variantmovector.hpp
template<typename... Types>
Variant<Types...>::Variant(Variant&& source)
{
if (!source.empty())
{
std::move(source).visit([&] (auto&& value) { *this = std::move(value); });
}
}
- 基于visit的實現(xiàn)也適用于模板化形式的拷貝和移動構造
// variant/variantcopyctortmpl.hpp
template<typename... Types>
template<typename... SourceTypes>
Variant<Types...>::Variant(const Variant<SourceTypes...>& source)
{
if (!source.empty())
{
source.visit([&] (const auto& value) { *this = value; });
}
}
// variant/variantmovectortmpl.hpp
template<typename... Types>
template<typename... SourceTypes>
Variant<Types...>::Variant(Variant<SourceTypes...>&& source)
{
if (!source.empty())
{
std::move(source).visit([&](auto&& value) { *this = std::move(value); });
}
}
Variant<short, float, const char*> v1((short)42);
Variant<int, std::string, double> v2(v1); // short到int的整型提升
std::cout << v2.get<int>(); // 42(int類型)
v1 = 3.14f;
Variant<double, int, std::string> v3(std::move(v1)); // float到double的浮點提升
std::cout << v3.get<double>(); // 3.14(double類型)
v1 = "hello";
Variant<double, int, std::string> v4(std::move(v1)); // const char*到std::string的轉換
std::cout << v4.get<std::string>(); // hello
賦值運算符
// variant/variantcopyassign.hpp
template<typename... Types>
Variant<Types...>& Variant<Types...>::operator=(const Variant& source)
{
if (!source.empty())
{
source.visit([&] (const auto& value) { *this = value; });
}
else
{
destroy(); // 源Variant為空時析構目標亦渗,隱式設置discriminator為0
}
return *this;
}
// variant/variantmoveassign.hpp
template<typename... Types>
Variant<Types...>& Variant<Types...>::operator= (Variant&& source)
{
if (!source.empty())
{
std::move(source).visit([&] (auto&& value) { *this = std::move(value); });
}
else
{
destroy();
}
return *this;
}
// variant/variantcopyassigntmpl.hpp
template<typename... Types>
template<typename... SourceTypes>
Variant<Types...>& Variant<Types...>::operator=(const Variant<SourceTypes...>& source)
{
if (!source.empty())
{
source.visit([&] (const auto& value) { *this = value; });
}
else
{
destroy();
}
return *this;
}
// variant/variantmoveassigntmpl.hpp
template<typename... Types>
template<typename... SourceTypes>
Variant<Types...>& Variant<Types...>::operator=(Variant<SourceTypes...>&& source)
{
if (!source.empty())
{
std::move(source).visit([&] (auto&& value) { *this = std::move(value); });
}
else
{
destroy();
}
return *this;
}