相关系列文章
目录
2.6.std::is_trivially_destructible
2.7.std::in_place_type和std::in_place_index
C++三剑客之std::variant(一) : 使用-CSDN博客
前一篇关于std::variant的博客详细介绍了std::variant的使用和一些注意事项,熟悉和了解它的使用方法后,我们接着追根溯源,探索其本质,仔细阅读它的实现,分析一下源码。
1.概述
本文我们主要研究问题模板类std::variant如何做到存任意多个类型值的容器?不同类型怎么做到巧妙的构造与转换的?多种构造函数如何实现?内部数据怎么储存?为什么不能保存引用、数组和void类型?
std::variant是在头文件variant中,是C++17引入的,本文以VS2019平台展开讲解variant的原理和深层次用法。
2.辅助类介绍
2.1.std::negation
std::negation 逻辑非元函数,一元函数对象类,其调用将返回对其实参求反的结果(由一元操作符-返回)。如:
- #include
- #include
-
- static_assert(
- std::is_same<
- std::bool_constant<false>,
- typename std::negation
true>>::type>::value, - "");
- static_assert(
- std::is_same<
- std::bool_constant<true>,
- typename std::negation
false>>::type>::value, - "");
-
- int main()
- {
- std::cout << std::negation
true>>::value << '\n'; //输出:false - std::cout << std::negation
false>>::value << '\n'; //输出:true - }
std::bool_constant
2.2.std::conjunction
std::conjunction 逻辑与元对象,在头文件type_traits中,一般用在判断可变参数是否满足某种条件上。示例如下:
- #include
- #include
-
- // func is enabled if all Ts... have the same type as T
- template<typename T, typename... Ts>
- std::enable_if_t
...>> - func(T, Ts...) {
- std::cout << "all types in pack are T\n";
- }
-
- // otherwise
- template<typename T, typename... Ts>
- std::enable_if_t...>>
- func(T, Ts...) {
- std::cout << "not all types in pack are T\n";
- }
-
- int main() {
- func(1, 2, 3);
- func(1, 2, "hello!");
- }
输出:
- all types in pack are T
- not all types in pack are T
上述代码在func中用std::is_same判断模板函数的参数类型是否都是一样的,所有参数类型一样判定为true,否则为false;同样std::variant的源码也用到了这个,如:
- template <class... _Types>
- using _Variant_storage = _Variant_storage_
...>, _Types...>;
std::conjunction就是判断可变参数对象是否都为简单销毁对象。
2.3.std::is_destructible
std::is_destructible 类型特征来检查一个类是否有可析构的类型。这有助于我们在编译时发现潜在的问题,例如试图删除非指针类型的对象。但它并不保证这个类型的析构函数是否真正做了正确的清理工作。因此,在定义类的析构函数时,我们需要仔细地考虑它是否真正释放了所有分配的资源。如下示例:
- #include
- #include
- #include
-
- class MyClass {
- public:
- MyClass(int size) : arr(new int[size]), file("example.txt") {}
- ~MyClass() { delete [] arr; }
-
- private:
- int* arr;
- std::ofstream file;
- };
-
- int main() {
- std::cout << std::is_destructible
::value << '\n'; //输出:true - std::cout << std::is_destructible<int>::value << '\n'; //输出: true
- std::cout << std::is_destructible<int[]>::value << '\n'; //输出:false
- std::cout << std::is_destructible
::value << '\n'; //输出:true - }
从上面的代码可以看出,我们定义的MyClass
类具有可析构的类型。而int
类型和std::ofstream
类型也是可析构的。但是,int[]
类型不是可析构的。这是因为数组类型不支持默认构造函数、拷贝构造函数或移动构造函数,从而导致不能正确地销毁。
2.4.std::is_object
std::is_object是一个用于元编程的C++类型特性,用于判断一个类型是否是对象类型,而不是类类型或枚举类型。这个在我之前的博客也讲的很清楚,如果还不是特别明白,可以再去翻翻博客C++之std::is_object-CSDN博客;在这里我就不多赘述了。
2.5.is_default_constructible
std::is_default_constructible模板,用于判断一个类型是否有默认构造函数。因为在某些情况下,需要在编译期间确定一个类型是否有默认构造函数。在使用该模板时需要包含头文件type_traits。示例代码:
- #include
- #include
-
- class X {
- public:
- X(int x): m_x(x) { }
- private:
- int m_x;
- };
-
- class Y {
- public:
- Y() = default;
- private:
- double m_y;
- };
-
- int main() {
- std::cout << std::is_default_constructible
::value << '\n'; //输出:false - std::cout << std::is_default_constructible
::value << '\n'; //输出:true - std::cout << std::is_default_constructible<int>::value << '\n'; //输出:true
- std::cout << std::is_default_constructible<int[]>::value << '\n'; //输出:false
- return 0;
- }
在上述示例代码中,我们定义了两个类X和Y,分别设置了构造函数和默认构造函数。然后分别使用is_default_constructible模板来判断是否有默认构造函数,最后还演示了一些基本类型和数组类型的情况。
2.6.std::is_trivially_destructible
判断一个类型T是否是一个平凡的可销毁类型(trivivally destructible)。主要用于检查这个类型的析构函数。一个trivivally destructible类(由class,struct/union)需要满足下面的条件:使用默认的析构函数、析构函数不能为虚的、它的基类和静态成员类型也必须是一个trivivally destructible类。如下示例:
- // is_trivially_destructible example
- #include
- #include
-
- struct A { }; /* 符合trivivally destructible类型定义 */
- struct B { ~B(){} }; /* 没有使用隐式应答的析构函数, 即编译器合成的默认析构函数, 因此不是trivivally destructible类型 */
-
- int main() {
- std::cout << std::boolalpha; /* 将输出流bool解析为true/false, 而不是1/0 */
- std::cout << "is_trivially_destructible:" << std::endl;
- std::cout << "int: " << std::is_trivially_destructible<int>::value << std::endl; /* 基本类型是trivivally destructible类型 */
- std::cout << "A: " << std::is_trivially_destructible::value << std::endl; /* A是trivivally destructible类型 */
- std::cout << "B: " << std::is_trivially_destructible::value << std::endl; /* B不是trivivally destructible类型 */
- return 0;
- }
输出:
- is_trivially_destructible:
- int: true
- A: true
- B: false
2.7.std::in_place_type和std::in_place_index
std::in_place_inde实际就是一个占位符,它的定义如下:
- template <size_t _Idx>
- inline constexpr in_place_index_t<_Idx> in_place_index{};
in_place_index_t 定义如下:
- template <size_t>
- struct in_place_index_t { // tag that selects the index of a type to construct in place
- explicit in_place_index_t() = default;
- };
从上面的代码可以看出 std::in_place_inde<_Idx> 是用来标识参数位置的数据类型,不过它是根据参数位置序号来判断的;同理也可以分析出std::in_place_type<_Ty>也是用来标识参数位置的数据类型,不过它是根据参数的类型来判断的,从std::in_place_type的定义可以看出来:
- struct in_place_t { // tag used to select a constructor which initializes a contained object in place
- explicit in_place_t() = default;
- };
- inline constexpr in_place_t in_place{};
-
- template <class>
- struct in_place_type_t { // tag that selects a type to construct in place
- explicit in_place_type_t() = default;
- };
- template <class _Ty>
- inline constexpr in_place_type_t<_Ty> in_place_type{};
3.原理分析
3.1.存储分析
std::variant的内部用了union递归存储各种类型的数据,在头文件variant中按码索骥找到了存储std::variant的类_Variant_storage,内部定义了一个union:
- template <bool _TrivialDestruction, class... _Types>
- class _Variant_storage_ {}; // empty storage (empty "_Types" case)
-
- // ALIAS TEMPLATE _Variant_storage
- template <class... _Types>
- using _Variant_storage = _Variant_storage_
...>, _Types...>; -
- template <class _First, class... _Rest>
- class _Variant_storage_<true, _First, _Rest...> { // Storage for variant alternatives (trivially destructible case)
- public:
- static constexpr size_t _Size = 1 + sizeof...(_Rest);
- union {
- remove_const_t<_First> _Head;
- _Variant_storage<_Rest...> _Tail;
- };
-
- _Variant_storage_() noexcept {} // no initialization (no active member)
-
- ...
- };
union自动按最大数据类型对齐的。std::variant的内存布局为:
第N个的_Tail为 _Variant_storage_
std::variant
_Variant_storage_根据对象是否为"简单销毁对象"划分为:
_Variant_storage_
- template <class... _Types>
- using _Variant_storage = _Variant_storage_
...>, _Types...>; -
- template <class _First, class... _Rest>
- class _Variant_storage_<true, _First, _Rest...> { // Storage for variant alternatives (trivially destructible case)
- public:
- static constexpr size_t _Size = 1 + sizeof...(_Rest);
- union {
- remove_const_t<_First> _Head;
- _Variant_storage<_Rest...> _Tail;
- };
-
- _Variant_storage_() noexcept {} // no initialization (no active member)
-
- template <class... _Types>
- constexpr explicit _Variant_storage_(integral_constant<size_t, 0>, _Types&&... _Args) noexcept(
- is_nothrow_constructible_v<_First, _Types...>)
- : _Head(static_cast<_Types&&>(_Args)...) {} // initialize _Head with _Args...
-
- template <size_t _Idx, class... _Types, enable_if_t<(_Idx > 0), int> = 0>
- constexpr explicit _Variant_storage_(integral_constant<size_t, _Idx>, _Types&&... _Args) noexcept(
- is_nothrow_constructible_v<_Variant_storage<_Rest...>, integral_constant<size_t, _Idx - 1>, _Types...>)
- : _Tail(integral_constant<size_t, _Idx - 1>{}, static_cast<_Types&&>(_Args)...) {} // initialize _Tail (recurse)
-
- _NODISCARD constexpr _First& _Get() & noexcept {
- return _Head;
- }
- _NODISCARD constexpr const _First& _Get() const& noexcept {
- return _Head;
- }
- _NODISCARD constexpr _First&& _Get() && noexcept {
- return _STD move(_Head);
- }
- _NODISCARD constexpr const _First&& _Get() const&& noexcept {
- return _STD move(_Head);
- }
- };
_Variant_storage_
- template <class _First, class... _Rest>
- class _Variant_storage_<false, _First, _Rest...> { // Storage for variant alternatives (non-trivially destructible case)
- public:
- static constexpr size_t _Size = 1 + sizeof...(_Rest);
- union {
- remove_const_t<_First> _Head;
- _Variant_storage<_Rest...> _Tail;
- };
-
- ~_Variant_storage_() noexcept { // explicitly non-trivial destructor (which would otherwise be defined as deleted
- // since the class has a variant member with a non-trivial destructor)
- }
-
- _Variant_storage_() noexcept {} // no initialization (no active member)
-
- template <class... _Types>
- constexpr explicit _Variant_storage_(integral_constant<size_t, 0>, _Types&&... _Args) noexcept(
- is_nothrow_constructible_v<_First, _Types...>)
- : _Head(static_cast<_Types&&>(_Args)...) {} // initialize _Head with _Args...
-
- template <size_t _Idx, class... _Types, enable_if_t<(_Idx > 0), int> = 0>
- constexpr explicit _Variant_storage_(integral_constant<size_t, _Idx>, _Types&&... _Args) noexcept(
- is_nothrow_constructible_v<_Variant_storage<_Rest...>, integral_constant<size_t, _Idx - 1>, _Types...>)
- : _Tail(integral_constant<size_t, _Idx - 1>{}, static_cast<_Types&&>(_Args)...) {} // initialize _Tail (recurse)
-
- _Variant_storage_(_Variant_storage_&&) = default;
- _Variant_storage_(const _Variant_storage_&) = default;
- _Variant_storage_& operator=(_Variant_storage_&&) = default;
- _Variant_storage_& operator=(const _Variant_storage_&) = default;
-
- _NODISCARD constexpr _First& _Get() & noexcept {
- return _Head;
- }
- _NODISCARD constexpr const _First& _Get() const& noexcept {
- return _Head;
- }
- _NODISCARD constexpr _First&& _Get() && noexcept {
- return _STD move(_Head);
- }
- _NODISCARD constexpr const _First&& _Get() const&& noexcept {
- return _STD move(_Head);
- }
- };
_Variant_storage_类中提供了对外访问对象的接口 _Get(),包括左值引用和右值引用。由于上层的类_Variant_base是private继承_Variant_storage_的,从下面的代码可以看出:
- template <class... _Types>
- class _Variant_base
- : private _Variant_storage<_Types...> { // Associate an integral discriminator with a _Variant_storage
- public:
- using _Index_t = _Variant_index_t<sizeof...(_Types)>;
- static constexpr auto _Invalid_index = static_cast<_Index_t>(-1);
- _Index_t _Which;
-
- using _Storage_t = _Variant_storage<_Types...>;
- _NODISCARD constexpr _Storage_t& _Storage() & noexcept { // access this variant's storage
- return *this;
- }
- _NODISCARD constexpr const _Storage_t& _Storage() const& noexcept { // access this variant's storage
- return *this;
- }
- _NODISCARD constexpr _Storage_t&& _Storage() && noexcept { // access this variant's storage
- return _STD move(*this);
- }
- _NODISCARD constexpr const _Storage_t&& _Storage() const&& noexcept { // access this variant's storage
- return _STD move(*this);
- }
-
- _Variant_base() noexcept : _Storage_t{}, _Which{_Invalid_index} {} // initialize to the value-less state
- ...
- };
由于上层的类不能访问_Variant_storage_的成员变量和函数,所以提供了专门的访问数据接口_Variant_raw_get,代码如下:
- template <size_t _Idx, class _Storage>
- _NODISCARD constexpr decltype(auto) _Variant_raw_get(
- _Storage&& _Obj) noexcept { // access the _Idx-th element of a _Variant_storage
- if constexpr (_Idx == 0) {
- return static_cast<_Storage&&>(_Obj)._Get();
- } else if constexpr (_Idx == 1) {
- return static_cast<_Storage&&>(_Obj)._Tail._Get();
- } else if constexpr (_Idx == 2) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Get();
- } else if constexpr (_Idx == 3) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Get();
- } else if constexpr (_Idx == 4) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Tail._Get();
- } else if constexpr (_Idx == 5) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Tail._Tail._Get();
- } else if constexpr (_Idx == 6) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Tail._Tail._Tail._Get();
- } else if constexpr (_Idx == 7) {
- return static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Get();
- } else if constexpr (_Idx < 16) {
- return _Variant_raw_get<_Idx - 8>(
- static_cast<_Storage&&>(_Obj)._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail);
- } else if constexpr (_Idx < 32) {
- return _Variant_raw_get<_Idx - 16>(
- static_cast<_Storage&&>(_Obj)
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail);
- } else if constexpr (_Idx < 64) {
- return _Variant_raw_get<_Idx - 32>(
- static_cast<_Storage&&>(_Obj)
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail);
- } else { // _Idx >= 64
- return _Variant_raw_get<_Idx - 64>(
- static_cast<_Storage&&>(_Obj)
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail
- ._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail);
- }
- }
函数中参数序号低于8的直接访问值,大于8的递归调用自身来访问值。
3.2.构造函数
3.2.1.默认构造
默认构造函数如下:
- template <class _First = _Meta_front
, enable_if_t, int> = 0> - constexpr variant() noexcept(is_nothrow_default_constructible_v<_First>)
- : _Mybase(in_place_index<0>) {} // value-initialize alternative 0
取出第一个参数_Meta_front,调用基类的构造函数,生成对象。象如下定义std::variant就会调用此构造函数:
std::variant<int, double, bool, float> y;
3.2.2.使用单一值初始化
示例如下:
std::variant<bool, int, std::string> v(25);
如果这样编码,就会直接调用std::variant的单一赋值的构造函数,源码如下:
- template <class _Ty,
- enable_if_t<sizeof...(_Types) != 0 //
- && !is_same_v<_Remove_cvref_t<_Ty>, variant> //
- && !_Is_specialization_v<_Remove_cvref_t<_Ty>, in_place_type_t> //
- && !_Is_in_place_index_specialization<_Remove_cvref_t<_Ty>> //
- && is_constructible_v<_Variant_init_type<_Ty, _Types...>, _Ty>, //
- int> = 0>
- constexpr variant(_Ty&& _Obj) noexcept(is_nothrow_constructible_v<_Variant_init_type<_Ty, _Types...>, _Ty>)
- : _Mybase(in_place_index<_Variant_init_index<_Ty, _Types...>::value>, static_cast<_Ty&&>(_Obj)) {
- // initialize to the type selected by passing _Obj to the overload set f(Types)...
- }
通过_Variant_init_index找到_Ty在_Types...的位置,然后再调用_Variant_base的构造函数:
- template <size_t _Idx, class... _UTypes,
- enable_if_t
, _Idx>, _UTypes...>, int> = 0> - constexpr explicit _Variant_base(in_place_index_t<_Idx>, _UTypes&&... _Args) noexcept(
- is_nothrow_constructible_v<_Meta_at_c
, _Idx>, _UTypes...>) - : _Storage_t(integral_constant<size_t, _Idx>{}, static_cast<_UTypes&&>(_Args)...),
- _Which{static_cast<_Index_t>(_Idx)} { // initialize alternative _Idx from _Args...
- }
初始化_Which和_Variant_storage,如果有多个可能的类型匹配,可能导致歧义。
3.2.3.std::in_place_type
示例如下:
std::variant<int, double, std::string> v(std::in_place_type<double>, 34.66);
如果这样编码,就会直接调用std::variant的std::in_place_type构造函数,源码如下:
- template <class _Ty, class... _UTypes, class _Idx = _Meta_find_unique_index
, - enable_if_t<_Idx::value != _Meta_npos && is_constructible_v<_Ty, _UTypes...>, int> = 0>
- constexpr explicit variant(in_place_type_t<_Ty>, _UTypes&&... _Args) noexcept(
- is_nothrow_constructible_v<_Ty, _UTypes...>) // strengthened
- : _Mybase(in_place_index<_Idx::value>, static_cast<_UTypes&&>(_Args)...) {
- // initialize alternative _Ty from _Args...
- }
通过_Meta_find_unique_index找到_Ty在_Types...的位置,_Meta_find_unique_index循环递归查找_Ty的详细实现:
- template <class _List, class _Ty>
- struct _Meta_find_unique_index_ {
- using type = integral_constant<size_t, _Meta_npos>;
- };
- template <class _List, class _Ty>
- using _Meta_find_unique_index =
- // The index of _Ty in _List if it occurs exactly once, otherwise _Meta_npos
- typename _Meta_find_unique_index_<_List, _Ty>::type;
-
- constexpr size_t _Meta_find_unique_index_i_2(const bool* const _Ptr, const size_t _Count,
- const size_t
- _First) { // return _First if there is no _First < j < _Count such that _Ptr[j] is true, otherwise _Meta_npos
- return _First != _Meta_npos && _Meta_find_index_i_(_Ptr, _Count, _First + 1) == _Meta_npos ? _First : _Meta_npos;
- }
-
- constexpr size_t _Meta_find_unique_index_i_(const bool* const _Ptr,
- const size_t _Count) { // Pass the smallest i such that _Ptr[i] is true to _Meta_find_unique_index_i_2
- return _Meta_find_unique_index_i_2(_Ptr, _Count, _Meta_find_index_i_(_Ptr, _Count));
- }
-
- template <template <class...> class _List, class _First, class... _Rest, class _Ty>
- struct _Meta_find_unique_index_<_List<_First, _Rest...>, _Ty> {
- using type = integral_constant<size_t,
- _Meta_find_unique_index_i_(_Meta_find_index_<_List<_First, _Rest...>, _Ty>::_Bools, 1 + sizeof...(_Rest))>;
- };
最后调用_Variant_base的构造函数,生成_Variant_storage,存储数据。
3.2.4.std::in_place_index
示例如下:
std::variant<bool, std::string> v(std::in_place_index<1>, "14256435");
如果这样编码,就会直接调用std::variant的std::in_place_index构造函数,源码如下:
- template <size_t _Idx, class... _UTypes,
- enable_if_t
, _UTypes...>, int> = 0> - constexpr explicit variant(in_place_index_t<_Idx>, _UTypes&&... _Args) noexcept(
- is_nothrow_constructible_v<_Meta_at_c
, _UTypes...>) // strengthened - : _Mybase(in_place_index<_Idx>, static_cast<_UTypes&&>(_Args)...) {
- // initialize alternative _Idx from _Args...
- }
直接调用_Variant_base的构造函数,这种生成std::vaiant流程会简单一些,也比较好理解一些。
3.3.访问值
3.3.1.直接赋值
如:
- std::variant<bool, int,std::string> v;
- v = "hello world";
如果这样编码,就会直接调用std::variant的operator=,源码如下:
- // assignment [variant.assign]
- template <class _Ty, enable_if_t, variant> //
- && is_constructible_v<_Variant_init_type<_Ty, _Types...>, _Ty> //
- && is_assignable_v<_Variant_init_type<_Ty, _Types...>&, _Ty>, //
- int> = 0>
- variant& operator=(_Ty&& _Obj) noexcept(is_nothrow_assignable_v<_Variant_init_type<_Ty, _Types...>&, _Ty>&&
- is_nothrow_constructible_v<_Variant_init_type<_Ty, _Types...>, _Ty>) {
- // assign/emplace the alternative chosen by overload resolution of _Obj with f(_Types)...
- constexpr size_t _TargetIdx = _Variant_init_index<_Ty, _Types...>::value;
- if (index() == _TargetIdx) {
- auto& _Target = _Variant_raw_get<_TargetIdx>(_Storage());
- _Target = static_cast<_Ty&&>(_Obj);
- } else {
- using _TargetTy = _Variant_init_type<_Ty, _Types...>;
- if constexpr (_Variant_should_directly_construct_v<_TargetTy, _Ty>) {
- this->_Reset();
- _Emplace_valueless<_TargetIdx>(static_cast<_Ty&&>(_Obj));
- } else {
- _TargetTy _Temp(static_cast<_Ty&&>(_Obj));
- this->_Reset();
- _Emplace_valueless<_TargetIdx>(_STD move(_Temp));
- }
- }
-
- return *this;
- }
它的流程如下:
关键步骤:1)比较当前的index()和_TargetIdx是否相同,相同,直接赋值。
2)不相同,这需要析构原来的对象,重新构造新的对象,赋值等操作,流程会比较复杂一些,这些实现是在_Emplace_valueless里面,代码如下:
- template <size_t _Idx, class... _ArgTypes>
- _Meta_at_c
& _Emplace_valueless(_ArgTypes&&... _Args) noexcept( - is_nothrow_constructible_v<_Meta_at_c
, _ArgTypes...>) { - // initialize alternative _Idx from _Args...
- // pre: valueless_by_exception()
- auto& _Obj = _Variant_raw_get<_Idx>(_Storage());
- _Construct_in_place(_Obj, static_cast<_ArgTypes&&>(_Args)...);
- this->_Set_index(_Idx);
- return _Obj;
- }
3.3.2.emplace
从emplace的源代码
- template <class _Ty, class... _ArgTypes, size_t _Idx = _Meta_find_unique_index
::value, - enable_if_t<_Idx != _Meta_npos && is_constructible_v<_Ty, _ArgTypes...>, int> = 0>
- _Ty& emplace(_ArgTypes&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _ArgTypes...>) /* strengthened */ {
- // emplace alternative _Ty from _Args...
- this->_Reset();
- return _Emplace_valueless<_Idx>(static_cast<_ArgTypes&&>(_Args)...);
- }
可以看出跟3.3.1的流程差不多,这里就不多赘述了。
3.3.3.get
1) 通过序号 index 来get值
- template <size_t _Idx, class... _Types>
- _NODISCARD constexpr decltype(auto) get(
- variant<_Types...>& _Var) { // access the contained value of _Var if its _Idx-th alternative is active
- static_assert(_Idx < sizeof...(_Types), "variant index out of bounds");
- if (_Var.index() == _Idx) {
- return _Variant_raw_get<_Idx>(_Var._Storage());
- }
-
- _Throw_bad_variant_access();
- }
通过当前的index()和_Idx比对,来获取std::variant的值,_Variant_raw_get函数在3.1章节讲过;
如果当前的index()不是_Idx,这会抛出异常。
2)通过 类型_Ty 来get值
- template <class _Ty, class... _Types>
- _NODISCARD constexpr decltype(auto) get(
- variant<_Types...>& _Var) { // access the contained value of _Var if its alternative _Ty is active
- constexpr size_t _Idx = _Meta_find_unique_index
, _Ty>::value; - static_assert(_Idx < sizeof...(_Types),
- "get
(variant&) requires T to occur exactly once in Types. (N4835 [variant.get]/5)" ); - return _STD get<_Idx>(_Var);
- }
通过_Meta_find_unique_index获取到类型_Ty的_Idx, 然后调用序号index版本的get来获取值。
3.3.4.get_if
get_if也是有两种,通过序号index和类型_Ty来获取值,从源码的
- template <size_t _Idx, class... _Types>
- _NODISCARD constexpr auto get_if(
- variant<_Types...>* _Ptr) noexcept { // get the address of *_Ptr's contained value if it holds alternative _Idx
- static_assert(_Idx < sizeof...(_Types), "variant index out of bounds");
- return _Ptr && _Ptr->index() == _Idx ? _STD addressof(_Variant_raw_get<_Idx>(_Ptr->_Storage())) : nullptr;
- }
和
- template <class _Ty, class... _Types>
- _NODISCARD constexpr add_pointer_t<_Ty> get_if(
- variant<_Types...>* _Ptr) noexcept { // get the address of *_Ptr's contained value if it holds alternative _Ty
- constexpr size_t _Idx = _Meta_find_unique_index
, _Ty>::value; - static_assert(_Idx != _Meta_npos,
- "get_if
(variant *) requires T to occur exactly once in Types. (N4835 [variant.get]/9)" ); - return _STD get_if<_Idx>(_Ptr);
- }
可以看到,基本上可以3.3.3的get原理差不多,在这里就不多赘述了。
4.总结
到此我们已经全部分析完毕,细节也谈及了,喜欢的给个赞并收藏,谢谢。
评论记录:
回复评论: