0%

[gcc13] STL源码解析 std::variant

总体结构

variant

首先是variant的结构,variant的模板声明长这样:

1
2
3
4
5
6
7
8
9
10
11
12
template<typename... _Types>
class variant
: private __detail::__variant::_Variant_base<_Types...>,
private _Enable_default_constructor<
__detail::__variant::_Traits<_Types...>::_S_default_ctor,
variant<_Types...>>,
private _Enable_copy_move<
__detail::__variant::_Traits<_Types...>::_S_copy_ctor,
__detail::__variant::_Traits<_Types...>::_S_copy_assign,
__detail::__variant::_Traits<_Types...>::_S_move_ctor,
__detail::__variant::_Traits<_Types...>::_S_move_assign,
variant<_Types...>>

_Enable_copy_move在上一篇optional解析中解释了,这里不同之处在于要考察variant的模板参数包内多个类型的可复制性、可移动性,如果其中某个类型不可复制或不可移动,那么variant亦不可复制或不可移动。
_Enable_default_constructor为用于生成默认构造的helper模板,代码长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<bool _Switch, typename _Tag = void>
struct _Enable_default_constructor
{
constexpr _Enable_default_constructor() noexcept = default;
constexpr _Enable_default_constructor(_Enable_default_constructor const&)
noexcept = default;
constexpr _Enable_default_constructor(_Enable_default_constructor&&)
noexcept = default;
_Enable_default_constructor&
operator=(_Enable_default_constructor const&) noexcept = default;
_Enable_default_constructor&
operator=(_Enable_default_constructor&&) noexcept = default;

// Can be used in other ctors.
constexpr explicit
_Enable_default_constructor(_Enable_default_constructor_tag) { }
};

当然它也有一个_Switchfalse的特化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename _Tag>
struct _Enable_default_constructor<false, _Tag>
{
constexpr _Enable_default_constructor() noexcept = delete;
constexpr _Enable_default_constructor(_Enable_default_constructor const&)
noexcept = default;
constexpr _Enable_default_constructor(_Enable_default_constructor&&)
noexcept = default;
_Enable_default_constructor&
operator=(_Enable_default_constructor const&) noexcept = default;
_Enable_default_constructor&
operator=(_Enable_default_constructor&&) noexcept = default;

// Can be used in other ctors.
constexpr explicit
_Enable_default_constructor(_Enable_default_constructor_tag) { }
};

对于variant其默认空构要求保有第一个类型的值,所以就要求第一个类型必须有默认空构:

1
2
3
4
5
6
7
8
9
10
11
template<typename... _Types>
struct _Traits
{
static constexpr bool _S_default_ctor =
is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
//...
}

template<typename _Tp0, typename... _Rest>
struct _Nth_type<0, _Tp0, _Rest...>
{ using type = _Tp0; };

_Variant_base

与其他stl模板类似,variant的实现主要在基类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<typename... _Types>
struct _Variant_base : _Move_assign_alias<_Types...>
{
using _Base = _Move_assign_alias<_Types...>;

constexpr
_Variant_base() noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)
: _Variant_base(in_place_index<0>) { }

template<size_t _Np, typename... _Args>
constexpr explicit
_Variant_base(in_place_index_t<_Np> __i, _Args&&... __args)
: _Base(__i, std::forward<_Args>(__args)...)
{ }

_Variant_base(const _Variant_base&) = default;
_Variant_base(_Variant_base&&) = default;
_Variant_base& operator=(const _Variant_base&) = default;
_Variant_base& operator=(_Variant_base&&) = default;
};

特殊成员函数套娃

这里存在几个套娃似的的结构用于解决实际存储的复制构造、移动构造、复制赋值、移动赋值问题,首先是移动赋值,_Variant_base继承的_Move_assign_alias相关代码:

1
2
3
4
5
6
7
8
9
10
11
template<typename... _Types>
using _Move_assign_alias =
_Move_assign_base<_Traits<_Types...>::_S_trivial_move_assign, _Types...>;

template<typename... _Types>
struct _Traits
{
static constexpr bool _S_trivial_move_assign =
_S_trivial_dtor && _S_trivial_move_ctor
&& (is_trivially_move_assignable_v<_Types> && ...);
}

_Move_assign_base按实际参数包是否可移动赋值,分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
template<bool, typename... _Types>
struct _Move_assign_base : _Copy_assign_alias<_Types...>
{
using _Base = _Copy_assign_alias<_Types...>;
using _Base::_Base;

_GLIBCXX20_CONSTEXPR
_Move_assign_base&
operator=(_Move_assign_base&& __rhs)
noexcept(_Traits<_Types...>::_S_nothrow_move_assign)
{
__variant::__raw_idx_visit(
[this](auto&& __rhs_mem, auto __rhs_index) mutable
{
constexpr size_t __j = __rhs_index;
if constexpr (__j != variant_npos)
{
if (this->_M_index == __j)
__variant::__get<__j>(*this) = std::move(__rhs_mem);
else
{
using _Tj = typename _Nth_type<__j, _Types...>::type;
if constexpr (is_nothrow_move_constructible_v<_Tj>)
__variant::__emplace<__j>(*this, std::move(__rhs_mem));
else
{
using _Variant = variant<_Types...>;
_Variant& __self = __variant_cast<_Types...>(*this);
__self.template emplace<__j>(std::move(__rhs_mem));
}
}
}
else
this->_M_reset();
}, __variant_cast<_Types...>(__rhs));
return *this;
}

_Move_assign_base(const _Move_assign_base&) = default;
_Move_assign_base(_Move_assign_base&&) = default;
_Move_assign_base& operator=(const _Move_assign_base&) = default;
};

template<typename... _Types>
struct _Move_assign_base<true, _Types...> : _Copy_assign_alias<_Types...>
{
using _Base = _Copy_assign_alias<_Types...>;
using _Base::_Base;
};

即在参数不可移动赋值时使用自定义的赋值操作,这里先不管这些移动、复制操作,先来看看最终的存储,省略中间套娃代码来到:

_Variant_storage

1
2
3
4
5
6
7
8
9
template<typename... _Types>
using _Variant_storage_alias =
_Variant_storage<_Traits<_Types...>::_S_trivial_dtor, _Types...>;

template<bool, typename... _Types>
struct _Copy_ctor_base : _Variant_storage_alias<_Types...>
{
//...
}

然后又到了是否平凡析构的环节…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
template<typename... _Types>
struct _Traits
{
static constexpr bool _S_trivial_dtor =
(is_trivially_destructible_v<_Types> && ...);
}

// Defines index and the dtor, possibly trivial.
template<bool __trivially_destructible, typename... _Types>
struct _Variant_storage;

template<typename... _Types>
struct _Variant_storage<false, _Types...>
{
constexpr
_Variant_storage()
: _M_index(static_cast<__index_type>(variant_npos))
{ }

template<size_t _Np, typename... _Args>
constexpr
_Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
: _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
_M_index{_Np}
{ }

constexpr void
_M_reset()
{
if (!_M_valid()) [[unlikely]]
return;

std::__do_visit<void>([](auto&& __this_mem) mutable
{
std::_Destroy(std::__addressof(__this_mem));
}, __variant_cast<_Types...>(*this));

_M_index = static_cast<__index_type>(variant_npos);
}

_GLIBCXX20_CONSTEXPR
~_Variant_storage()
{ _M_reset(); }

constexpr bool
_M_valid() const noexcept
{
if constexpr (__variant::__never_valueless<_Types...>())
return true;
return this->_M_index != __index_type(variant_npos);
}

_Variadic_union<_Types...> _M_u;
using __index_type = __select_index<_Types...>;
__index_type _M_index;
};

template<typename... _Types>
struct _Variant_storage<true, _Types...>
{
constexpr
_Variant_storage()
: _M_index(static_cast<__index_type>(variant_npos))
{ }

template<size_t _Np, typename... _Args>
constexpr
_Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
: _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
_M_index{_Np}
{ }

constexpr void
_M_reset() noexcept
{ _M_index = static_cast<__index_type>(variant_npos); }

constexpr bool
_M_valid() const noexcept
{
if constexpr (__variant::__never_valueless<_Types...>())
return true;
// It would be nice if we could just return true for -fno-exceptions.
// It's possible (but inadvisable) that a std::variant could become
// valueless in a translation unit compiled with -fexceptions and then
// be passed to functions compiled with -fno-exceptions. We would need
// some #ifdef _GLIBCXX_NO_EXCEPTIONS_GLOBALLY property to elide all
// checks for valueless_by_exception().
return this->_M_index != static_cast<__index_type>(variant_npos);
}

_Variadic_union<_Types...> _M_u;
using __index_type = __select_index<_Types...>;
__index_type _M_index;
};

两个特化区别在于析构时的选择,但两个都包含相同的数据成员_M_u_M_index,先来看这个_Variadic_union

_Variadic_union

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  // Defines members and ctors.
template<typename... _Types>
union _Variadic_union
{
_Variadic_union() = default;

template<size_t _Np, typename... _Args>
_Variadic_union(in_place_index_t<_Np>, _Args&&...) = delete;
};

template<typename _First, typename... _Rest>
union _Variadic_union<_First, _Rest...>
{
constexpr _Variadic_union() : _M_rest() { }

template<typename... _Args>
constexpr
_Variadic_union(in_place_index_t<0>, _Args&&... __args)
: _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
{ }

template<size_t _Np, typename... _Args>
constexpr
_Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
: _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
{ }

#if __cpp_lib_variant >= 202106L
_Variadic_union(const _Variadic_union&) = default;
_Variadic_union(_Variadic_union&&) = default;
_Variadic_union& operator=(const _Variadic_union&) = default;
_Variadic_union& operator=(_Variadic_union&&) = default;

~_Variadic_union() = default;

constexpr ~_Variadic_union()
requires (!__has_trivial_destructor(_First))
|| (!__has_trivial_destructor(_Variadic_union<_Rest...>))
{ }
#endif

_Uninitialized<_First> _M_first;
_Variadic_union<_Rest...> _M_rest;
};

这是个递归定义的template union,上面的_Variadic_union是无类型参数时的template,下面的则是递归定义。_Uninitialized<_First> _M_first是递归定义中这一层的实际值,_Variadic_union<_Rest...> _M_rest递归定义剩余参数包,_M_first_M_rest共享一块内存。
来看看_Uninitialized

_Uninitialized

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  // For C++17:
// _Uninitialized<T> is guaranteed to be a trivially destructible type,
// even if T is not.
// For C++20:
// _Uninitialized<T> is trivially destructible iff T is, so _Variant_union
// needs a constrained non-trivial destructor.
template<typename _Type, bool = std::is_trivially_destructible_v<_Type>>
struct _Uninitialized;

template<typename _Type>
struct _Uninitialized<_Type, true>
{
template<typename... _Args>
constexpr
_Uninitialized(in_place_index_t<0>, _Args&&... __args)
: _M_storage(std::forward<_Args>(__args)...)
{ }

constexpr const _Type& _M_get() const & noexcept
{ return _M_storage; }

constexpr _Type& _M_get() & noexcept
{ return _M_storage; }

constexpr const _Type&& _M_get() const && noexcept
{ return std::move(_M_storage); }

constexpr _Type&& _M_get() && noexcept
{ return std::move(_M_storage); }

_Type _M_storage;
};

template<typename _Type>
struct _Uninitialized<_Type, false>
{
#if __cpp_lib_variant >= 202106L
template<typename... _Args>
constexpr
_Uninitialized(in_place_index_t<0>, _Args&&... __args)
: _M_storage(std::forward<_Args>(__args)...)
{ }

constexpr ~_Uninitialized() { }

_Uninitialized(const _Uninitialized&) = default;
_Uninitialized(_Uninitialized&&) = default;
_Uninitialized& operator=(const _Uninitialized&) = default;
_Uninitialized& operator=(_Uninitialized&&) = default;

constexpr const _Type& _M_get() const & noexcept
{ return _M_storage; }

constexpr _Type& _M_get() & noexcept
{ return _M_storage; }

constexpr const _Type&& _M_get() const && noexcept
{ return std::move(_M_storage); }

constexpr _Type&& _M_get() && noexcept
{ return std::move(_M_storage); }

struct _Empty_byte { };

union {
_Empty_byte _M_empty;
_Type _M_storage;
};
#else
template<typename... _Args>
constexpr
_Uninitialized(in_place_index_t<0>, _Args&&... __args) // ①
{
::new ((void*)std::addressof(_M_storage))
_Type(std::forward<_Args>(__args)...);
}

const _Type& _M_get() const & noexcept
{ return *_M_storage._M_ptr(); }

_Type& _M_get() & noexcept
{ return *_M_storage._M_ptr(); }

const _Type&& _M_get() const && noexcept
{ return std::move(*_M_storage._M_ptr()); }

_Type&& _M_get() && noexcept
{ return std::move(*_M_storage._M_ptr()); }

__gnu_cxx::__aligned_membuf<_Type> _M_storage;
#endif
};

根据类型是否可平凡析构特化了两个版本的_Uninitialized,可平凡析构时_Uninitialized即是类型_Type的简单封装;
不可平凡析构且标准为c++17时实际存储为__gnu_cxx::__aligned_membuf<_Type>__aligned_membuf是gcc内常用的内存对齐缓冲,该类型内包含一个实际存储对象的字节数组,构造、析构都不操作实际对象,所以上述代码①处使用了placement new构造实际对象,当前场景的_Uninitialized也由编译器生成平凡析构不对实际对象进行析构;
不可平凡析构且为c++20以后标准时,这里由于c++20标准后constexpr变量必须拥有constexpr析构(所谓常量析构),__aligned_membuf不具备常量析构,所以这里另写了个分支。该场景下使用一个带有_Empty_byte的union控制存储,并提供一个非平凡的常量析构,且这个析构并不析构内在对象,实际对象的析构由外部控制。
所以_Variadic_union即一个可变参数union,不难猜出_Variant_storage中的__index_type即是表明当前union存在项的索引,这里展开下前面的using __index_type = __select_index<_Types...>,把相关代码贴下:

__select_index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  template <typename... _Types>
using __select_index =
typename __select_int::_Select_int_base<sizeof...(_Types),
unsigned char,
unsigned short>::type::value_type;

template<unsigned long long _Val, typename _IntType, typename... _Ints>
struct _Select_int_base<_Val, _IntType, _Ints...>
: __conditional_t<(_Val <= __gnu_cxx::__int_traits<_IntType>::__max),
integral_constant<_IntType, (_IntType)_Val>,
_Select_int_base<_Val, _Ints...>>
{ };

template<bool _Cond, typename _If, typename _Else>
using __conditional_t
= typename __conditional<_Cond>::template type<_If, _Else>;

template<bool>
struct __conditional
{
template<typename _Tp, typename>
using type = _Tp;
};

template<>
struct __conditional<false>
{
template<typename, typename _Up>
using type = _Up;
};

/// integral_constant
template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
constexpr operator value_type() const noexcept { return value; }
#if __cplusplus > 201103L

#define __cpp_lib_integral_constant_callable 201304L

constexpr value_type operator()() const noexcept { return value; }
#endif
};

struct _Select_int_base<sizeof...(_Types), unsigned char, unsigned short>
: __conditional_t<(sizeof...(_Types) <= __gnu_cxx::__int_traits<_unsigned char>::__max),
integral_constant<unsigned char, (unsigned char)sizeof...(_Types)>,
_Select_int_base<sizeof...(_Types), unsigned short>>

__conditional_t是条件模板,即当第一个模板参数成立时该模板为_If否则为_Else,亦即当参数包所占小于unsigned char最大值时继承integral_constant<unsigned char, (unsigned char)sizeof...(_Types)>否则继承integral_constant<unsigned short, (unsigned short)sizeof...(_Types)>,而integral_constant<_Tp, __v>::type::value_type其实也就是_Tp。所以__index_type类型即是unsigned char或是unsigned short

结构图

到此已经差不多把std::variant结构过了一遍,这里以std::variant<int, float, string> va{std::in_place_index<2>, "str in variant"}为例,来看看gdb中看到的结构:

三个_Variadic_union共享内存:

成员函数

构造函数

默认构造

继承链底层的variant将五个特殊成员及空构都声明为=default

1
2
3
4
5
6
variant() = default;
variant(const variant& __rhs) = default;
variant(variant&&) = default;
variant& operator=(const variant&) = default;
variant& operator=(variant&&) = default;
_GLIBCXX20_CONSTEXPR ~variant() = default;

默认空构时构造父类_Variant_base

1
2
3
4
5
6
7
8
9
10
using _Base = _Move_assign_alias<_Types...>;
constexpr
_Variant_base() noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)
: _Variant_base(in_place_index<0>) { }

template<size_t _Np, typename... _Args>
constexpr explicit
_Variant_base(in_place_index_t<_Np> __i, _Args&&... __args)
: _Base(__i, std::forward<_Args>(__args)...)
{ }

中间的_Move_assign_alias四个套娃类只各自解决一个特殊成员函数,最终"_Base"的构造会来到_Variant_storage

1
2
3
4
5
6
template<size_t _Np, typename... _Args>
constexpr
_Variant_storage(in_place_index_t<_Np>, _Args&&... __args)
: _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...),
_M_index{_Np}
{ }

构造_Variadic_union

1
2
3
4
5
template<typename... _Args>
constexpr
_Variadic_union(in_place_index_t<0>, _Args&&... __args)
: _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
{ }

构造_Uninitialized

1
2
3
4
5
template<typename... _Args>
constexpr
_Uninitialized(in_place_index_t<0>, _Args&&... __args)
: _M_storage(std::forward<_Args>(__args)...)
{ }

默认构造时构造第一个元素,_Variant_storage_M_index构造为0。

in_place_index构造

来看看另一个指定index的构造,variant

1
2
3
4
5
6
7
8
template<size_t _Np, typename... _Args,
typename _Tp = __to_type<_Np>,
typename = enable_if_t<is_constructible_v<_Tp, _Args...>>>
constexpr explicit
variant(in_place_index_t<_Np>, _Args&&... __args)
: _Base(in_place_index<_Np>, std::forward<_Args>(__args)...),
_Default_ctor_enabler(_Enable_default_constructor_tag{})
{ }

_Default_ctor_enabler是上面提到的_Enable_default_constructor的别名,这里就是标识这个父类是经由非默认构造函数进行构造。
_Base的构造和默认构造类似,只是这里的_Np是由函数模板参数指定,到_Variadic_union中时经由另一个函数模板来完成构造:

1
2
3
4
5
template<size_t _Np, typename... _Args>
constexpr
_Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
: _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
{ }

这样在多嵌套层_Variadic_union中经由多次模板函数调用找到对应index的_M_first完成构造。

析构函数

主要析构在于_Variant_storage中,关注下当实际类型不可平凡析构时_Variant_storage<false, _Types...>的析构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
constexpr void
_M_reset()
{
if (!_M_valid()) [[unlikely]]
return;

std::__do_visit<void>([](auto&& __this_mem) mutable
{
std::_Destroy(std::__addressof(__this_mem));
}, __variant_cast<_Types...>(*this));

_M_index = static_cast<__index_type>(variant_npos);
}

_GLIBCXX20_CONSTEXPR
~_Variant_storage()
{ _M_reset(); }

__do_visit即是gcc内std::visit的实现;_Destroy也是gcc内调用析构的方法,在17标准后调用std::destroy_at进行析构;std::__addressof同为gcc实现内的方法,可以视为std::addressof;所以这里的逻辑也比较清晰了,调用visit方法访问实际对象,泛型lambda内实参类型确定后利用addressofdestroy_at对指定类型析构。
variant_nposconstexpr size_t固定为-1。

非成员函数

std::visit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
template<typename _Visitor, typename... _Variants>
constexpr __detail::__variant::__visit_result_t<_Visitor, _Variants...>
visit(_Visitor&& __visitor, _Variants&&... __variants)
{
namespace __variant = std::__detail::__variant;

if ((__variant::__as(__variants).valueless_by_exception() || ...))
__throw_bad_variant_access("std::visit: variant is valueless");

using _Result_type
= __detail::__variant::__visit_result_t<_Visitor, _Variants...>;

using _Tag = __detail::__variant::__deduce_visit_result<_Result_type>;

if constexpr (sizeof...(_Variants) == 1)
{
using _Vp = decltype(__variant::__as(std::declval<_Variants>()...));

constexpr bool __visit_rettypes_match = __detail::__variant::
__check_visitor_results<_Visitor, _Vp>(
make_index_sequence<variant_size_v<remove_reference_t<_Vp>>>());
if constexpr (!__visit_rettypes_match)
{
static_assert(__visit_rettypes_match,
"std::visit requires the visitor to have the same "
"return type for all alternatives of a variant");
return;
}
else
return std::__do_visit<_Tag>(
std::forward<_Visitor>(__visitor),
static_cast<_Vp>(__variants)...);
}
else
return std::__do_visit<_Tag>(
std::forward<_Visitor>(__visitor),
__variant::__as(std::forward<_Variants>(__variants))...);
}

这里先调用valueless_by_exception校验variant是否有值,再检查参数包_Variants内参数个数,个数为1时校验了_Visitor对于_Variants内所有成员返回值是否一致,再调用实现方法__do_visit,这里元编程内容很多,鉴于此篇篇幅就不一一解读了。再来看下__do_visit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  template<typename _Result_type, typename _Visitor, typename... _Variants>
constexpr decltype(auto)
__do_visit(_Visitor&& __visitor, _Variants&&... __variants)
{
// Get the silly case of visiting no variants out of the way first.
if constexpr (sizeof...(_Variants) == 0)
{
if constexpr (is_void_v<_Result_type>)
return (void) std::forward<_Visitor>(__visitor)();
else
return std::forward<_Visitor>(__visitor)();
}
else
{
constexpr size_t __max = 11; // "These go to eleven."

// The type of the first variant in the pack.
using _V0 = typename _Nth_type<0, _Variants...>::type;
// The number of alternatives in that first variant.
constexpr auto __n = variant_size_v<remove_reference_t<_V0>>;

if constexpr (sizeof...(_Variants) > 1 || __n > __max)
{
// Use a jump table for the general case.
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;

auto __func_ptr = __vtable._M_access(__variants.index()...);
return (*__func_ptr)(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
}
else // We have a single variant with a small number of alternatives.
{
// A name for the first variant in the pack.
_V0& __v0
= [](_V0& __v, ...) -> _V0& { return __v; }(__variants...);

using __detail::__variant::_Multi_array;
using __detail::__variant::__gen_vtable_impl;
using _Ma = _Multi_array<_Result_type (*)(_Visitor&&, _V0&&)>;

#ifdef _GLIBCXX_DEBUG
# define _GLIBCXX_VISIT_UNREACHABLE __builtin_trap
#else
# define _GLIBCXX_VISIT_UNREACHABLE __builtin_unreachable
#endif

#define _GLIBCXX_VISIT_CASE(N) \
case N: \
{ \
if constexpr (N < __n) \
{ \
return __gen_vtable_impl<_Ma, index_sequence<N>>:: \
__visit_invoke(std::forward<_Visitor>(__visitor), \
std::forward<_V0>(__v0)); \
} \
else _GLIBCXX_VISIT_UNREACHABLE(); \
}

switch (__v0.index())
{
_GLIBCXX_VISIT_CASE(0)
_GLIBCXX_VISIT_CASE(1)
_GLIBCXX_VISIT_CASE(2)
_GLIBCXX_VISIT_CASE(3)
_GLIBCXX_VISIT_CASE(4)
_GLIBCXX_VISIT_CASE(5)
_GLIBCXX_VISIT_CASE(6)
_GLIBCXX_VISIT_CASE(7)
_GLIBCXX_VISIT_CASE(8)
_GLIBCXX_VISIT_CASE(9)
_GLIBCXX_VISIT_CASE(10)
case variant_npos:
using __detail::__variant::__variant_idx_cookie;
using __detail::__variant::__variant_cookie;
if constexpr (is_same_v<_Result_type, __variant_idx_cookie>
|| is_same_v<_Result_type, __variant_cookie>)
{
using _Npos = index_sequence<variant_npos>;
return __gen_vtable_impl<_Ma, _Npos>::
__visit_invoke(std::forward<_Visitor>(__visitor),
std::forward<_V0>(__v0));
}
else
_GLIBCXX_VISIT_UNREACHABLE();
default:
_GLIBCXX_VISIT_UNREACHABLE();
}
#undef _GLIBCXX_VISIT_CASE
#undef _GLIBCXX_VISIT_UNREACHABLE
}
}
}

元编程内容依旧非常多,也不一一解读了,大致逻辑就是在单个variant元素大于11个或者要访问的variant多于1个时使用跳表访问每个variant内容,否则就通过第一个variantindex进行访问。

std::holds_alternative

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename _Tp, typename... _Types>
constexpr bool
holds_alternative(const variant<_Types...>& __v) noexcept
{
static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
"T must occur exactly once in alternatives");
return __v.index() == std::__find_uniq_type_in_pack<_Tp, _Types...>();
}

// Return the index of _Tp in _Types, if it occurs exactly once.
// Otherwise, return sizeof...(_Types).
template<typename _Tp, typename... _Types>
constexpr size_t
__find_uniq_type_in_pack()
{
constexpr size_t __sz = sizeof...(_Types);
constexpr bool __found[__sz] = { __is_same(_Tp, _Types) ... };
size_t __n = __sz;
for (size_t __i = 0; __i < __sz; ++__i)
{
if (__found[__i])
{
if (__n < __sz) // more than one _Tp found
return __sz;
__n = __i;
}
}
return __n;
}

元编程的技巧,利用推导将variant模板参数包拿到再利用该参数包找到指定类型的index。

std::get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
template<typename _Tp, typename... _Types>
constexpr _Tp&
get(variant<_Types...>& __v)
{
static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>,
"T must occur exactly once in alternatives");
constexpr size_t __n = std::__find_uniq_type_in_pack<_Tp, _Types...>();
return std::get<__n>(__v);
}

template<size_t _Np, typename... _Types>
constexpr variant_alternative_t<_Np, variant<_Types...>>&
get(variant<_Types...>& __v)
{
static_assert(_Np < sizeof...(_Types),
"The index must be in [0, number of alternatives)");
if (__v.index() != _Np)
__throw_bad_variant_access(__v.valueless_by_exception());
return __detail::__variant::__get<_Np>(__v);
}

// Returns the typed storage for __v.
template<size_t _Np, typename _Variant>
constexpr decltype(auto)
__get(_Variant&& __v) noexcept
{ return __variant::__get_n<_Np>(std::forward<_Variant>(__v)._M_u); }

template<size_t _Np, typename _Union>
constexpr decltype(auto)
__get_n(_Union&& __u) noexcept
{
if constexpr (_Np == 0)
return std::forward<_Union>(__u)._M_first._M_get();
else if constexpr (_Np == 1)
return std::forward<_Union>(__u)._M_rest._M_first._M_get();
else if constexpr (_Np == 2)
return std::forward<_Union>(__u)._M_rest._M_rest._M_first._M_get();
else
return __variant::__get_n<_Np - 3>(
std::forward<_Union>(__u)._M_rest._M_rest._M_rest);
}

摘取了部分代码在上面,以std::get<ClassName>形式调用时利用上面holds_alternative提到的技巧找到index,最后递归找到实际index的_Uninitialized