0%

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

std::optional是17标准中增加的管理可选值的类模板,以往经验中如果某个函数的返回值可能失败,如何判断这个失败值和传递这个失败值都需要些额外的手段,optional的出现则很好的解决了这个问题。以下是gcc13版本中std::optional源码的个人解析:

std::optional实现内各类型关系

optional

首先是optional模板声明及内部别名:

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
/**
* @brief Class template for optional values.
*/
template<typename _Tp>
class optional
: private _Optional_base<_Tp>,
private _Enable_copy_move<
// Copy constructor.
is_copy_constructible_v<_Tp>,
// Copy assignment.
__and_v<is_copy_constructible<_Tp>, is_copy_assignable<_Tp>>,
// Move constructor.
is_move_constructible_v<_Tp>,
// Move assignment.
__and_v<is_move_constructible<_Tp>, is_move_assignable<_Tp>>,
// Unique tag type.
optional<_Tp>>
{
static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>);
static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>);
static_assert(is_object_v<_Tp> && !is_array_v<_Tp>);

private:
using _Base = _Optional_base<_Tp>;

// SFINAE helpers
template<typename _Up>
using __not_self = __not_<is_same<optional, __remove_cvref_t<_Up>>>;
template<typename _Up>
using __not_tag = __not_<is_same<in_place_t, __remove_cvref_t<_Up>>>;
template<typename... _Cond>
using _Requires = enable_if_t<__and_v<_Cond...>, bool>;

_Enable_copy_move为gcc内部的一个helper模板,四个模板参数对应于是否有复制构造、复制赋值、移动构造、移动赋值,其定义和其中一个特化长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @brief A mixin helper to conditionally enable or disable the copy/move
* special members.
* @sa _Enable_special_members
*/
template<bool _Copy, bool _CopyAssignment,
bool _Move, bool _MoveAssignment,
typename _Tag = void>
struct _Enable_copy_move { };

template<typename _Tag>
struct _Enable_copy_move<false, true, true, true, _Tag>
{
constexpr _Enable_copy_move() noexcept = default;
constexpr _Enable_copy_move(_Enable_copy_move const&) noexcept = delete;
constexpr _Enable_copy_move(_Enable_copy_move&&) noexcept = default;
_Enable_copy_move&
operator=(_Enable_copy_move const&) noexcept = default;
_Enable_copy_move&
operator=(_Enable_copy_move&&) noexcept = default;
};

由于子类optional<T>内没有没有数据成员固没有声明任何五个特殊成员函数,所以在这里的含义即是如果optional<T>T不拥有复制构造则optional<T>因该helper父类没有复制构造亦不拥有复制构造,其他三个特殊成员函数同理。

_Optional_base

optional<T>子类没有数据成员,成员方法皆为父类方法的封装,所以实现还要看这个_Optional_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
template<typename _Tp,
bool = is_trivially_copy_constructible_v<_Tp>,
bool = is_trivially_move_constructible_v<_Tp>>
struct _Optional_base
: _Optional_base_impl<_Tp, _Optional_base<_Tp>>
{
// Constructors for disengaged optionals.
constexpr _Optional_base() = default;

// Constructors for engaged optionals.
template<typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Args...>, bool> = false>
constexpr explicit
_Optional_base(in_place_t, _Args&&... __args)
: _M_payload(in_place, std::forward<_Args>(__args)...)
{ }

template<typename _Up, typename... _Args,
enable_if_t<is_constructible_v<_Tp,
initializer_list<_Up>&,
_Args...>, bool> = false>
constexpr explicit
_Optional_base(in_place_t,
initializer_list<_Up> __il,
_Args&&... __args)
: _M_payload(in_place, __il, std::forward<_Args>(__args)...)
{ }

// Copy and move constructors.
constexpr
_Optional_base(const _Optional_base& __other)
: _M_payload(__other._M_payload._M_engaged, __other._M_payload)
{ }

constexpr
_Optional_base(_Optional_base&& __other)
noexcept(is_nothrow_move_constructible_v<_Tp>)
: _M_payload(__other._M_payload._M_engaged,
std::move(__other._M_payload))
{ }

// Assignment operators.
_Optional_base& operator=(const _Optional_base&) = default;
_Optional_base& operator=(_Optional_base&&) = default;

_Optional_payload<_Tp> _M_payload;
};

_Optional_base内包含实际存储对象的_Optional_payload<_Tp> _M_payload,这里为了让这个类型拥有和_Tp同样的“可平凡复制构造性”、“可平凡移动构造性”,特化了其他三个版本(_Optional_base<_Tp, false, true>,_Optional_base<_Tp, true, false>,_Optional_base<_Tp, true, true>)声明该类的复制构造、移动构造,比如_Optional_base<_Tp, false, true>

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
template<typename _Tp>
struct _Optional_base<_Tp, false, true>
: _Optional_base_impl<_Tp, _Optional_base<_Tp>>
{
// Constructors for disengaged optionals.
constexpr _Optional_base() = default;

// Constructors for engaged optionals.
template<typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Args...>, bool> = false>
constexpr explicit
_Optional_base(in_place_t, _Args&&... __args)
: _M_payload(in_place, std::forward<_Args>(__args)...)
{ }

template<typename _Up, typename... _Args,
enable_if_t<is_constructible_v<_Tp,
initializer_list<_Up>&,
_Args...>, bool> = false>
constexpr explicit
_Optional_base(in_place_t,
initializer_list<_Up> __il,
_Args... __args)
: _M_payload(in_place, __il, std::forward<_Args>(__args)...)
{ }

// Copy and move constructors.
constexpr _Optional_base(const _Optional_base& __other)
: _M_payload(__other._M_payload._M_engaged, __other._M_payload)
{ }

constexpr _Optional_base(_Optional_base&& __other) = default;

// Assignment operators.
_Optional_base& operator=(const _Optional_base&) = default;
_Optional_base& operator=(_Optional_base&&) = default;

_Optional_payload<_Tp> _M_payload;
};

可以看到和开始的模板类相比仅是复制构造被声明为了=default,开始的模板类其实也就是_Optional_base<_Tp, false, false>。这里为了不写很多相同的代码,把涉及_M_payload的很多封装操作封装进了_Optional_base_impl,而_M_payload也通过模板参数_Optional_base_impl<_Tp, _Optional_base<_Tp>>从子类传递给了父类。

_Optional_base_impl

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
// Common base class for _Optional_base<T> to avoid repeating these
// member functions in each specialization.
template<typename _Tp, typename _Dp>
class _Optional_base_impl
{
protected:
using _Stored_type = remove_const_t<_Tp>;

// The _M_construct operation has !_M_engaged as a precondition
// while _M_destruct has _M_engaged as a precondition.
template<typename... _Args>
constexpr void
_M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{
static_cast<_Dp*>(this)->_M_payload._M_construct(
std::forward<_Args>(__args)...);
}

constexpr void
_M_destruct() noexcept
{ static_cast<_Dp*>(this)->_M_payload._M_destroy(); }

// _M_reset is a 'safe' operation with no precondition.
constexpr void
_M_reset() noexcept
{ static_cast<_Dp*>(this)->_M_payload._M_reset(); }

constexpr bool _M_is_engaged() const noexcept
{ return static_cast<const _Dp*>(this)->_M_payload._M_engaged; }

// The _M_get operations have _M_engaged as a precondition.
constexpr _Tp&
_M_get() noexcept
{
__glibcxx_assert(this->_M_is_engaged());
return static_cast<_Dp*>(this)->_M_payload._M_get();
}

constexpr const _Tp&
_M_get() const noexcept
{
__glibcxx_assert(this->_M_is_engaged());
return static_cast<const _Dp*>(this)->_M_payload._M_get();
}
};

从代码开始的注释中也印证我的推断,_Optional_base_impl的存在只是为了减少各特化版本中的重复代码。作为父类的_Optional_base_impl通过static_cast<_Dp*>(this)->_M_payload来调用子类内的_M_payload

_Optional_payload

实际存储值的_Optional_payload声明:

1
2
3
4
5
6
7
8
9
10
11
// Class template that manages the payload for optionals.
template <typename _Tp,
bool /*_HasTrivialDestructor*/ =
is_trivially_destructible_v<_Tp>,
bool /*_HasTrivialCopy */ =
is_trivially_copy_assignable_v<_Tp>
&& is_trivially_copy_constructible_v<_Tp>,
bool /*_HasTrivialMove */ =
is_trivially_move_assignable_v<_Tp>
&& is_trivially_move_constructible_v<_Tp>>
struct _Optional_payload;

又是可平凡析构、可平凡复制、可平凡移动的模板参数,这个实际值管理的子类同样视实际值的平凡性对应不同的_Optional_payload

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
// Payload for potentially-constexpr optionals (trivial copy/move/destroy).
template <typename _Tp>
struct _Optional_payload<_Tp, true, true, true>
: _Optional_payload_base<_Tp>
{
using _Optional_payload_base<_Tp>::_Optional_payload_base;

_Optional_payload() = default;
};

// Payload for optionals with non-trivial copy construction/assignment.
template <typename _Tp>
struct _Optional_payload<_Tp, true, false, true>
: _Optional_payload_base<_Tp>
{
using _Optional_payload_base<_Tp>::_Optional_payload_base;

_Optional_payload() = default;
~_Optional_payload() = default;
_Optional_payload(const _Optional_payload&) = default;
_Optional_payload(_Optional_payload&&) = default;
_Optional_payload& operator=(_Optional_payload&&) = default;

// Non-trivial copy assignment.
constexpr
_Optional_payload&
operator=(const _Optional_payload& __other)
{
this->_M_copy_assign(__other);
return *this;
}
};

// Payload for optionals with non-trivial move construction/assignment.
template <typename _Tp>
struct _Optional_payload<_Tp, true, true, false>
: _Optional_payload_base<_Tp>
{
using _Optional_payload_base<_Tp>::_Optional_payload_base;

_Optional_payload() = default;
~_Optional_payload() = default;
_Optional_payload(const _Optional_payload&) = default;
_Optional_payload(_Optional_payload&&) = default;
_Optional_payload& operator=(const _Optional_payload&) = default;

// Non-trivial move assignment.
constexpr
_Optional_payload&
operator=(_Optional_payload&& __other)
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_assignable<_Tp>>)
{
this->_M_move_assign(std::move(__other));
return *this;
}
};

可以看出区别在于非平凡复制、非平凡移动时有特殊的复制赋值、移动赋值,先不管这里为什么这么写,看看这个基类_Optional_payload_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
// This class template manages construction/destruction of
// the contained value for a std::optional.
template <typename _Tp>
struct _Optional_payload_base
{
using _Stored_type = remove_const_t<_Tp>;

_Optional_payload_base() = default;
~_Optional_payload_base() = default;
//....

struct _Empty_byte { };

template<typename _Up, bool = is_trivially_destructible_v<_Up>>
union _Storage
{
//...
_Empty_byte _M_empty;
_Up _M_value;
};

_Storage<_Stored_type> _M_payload;

bool _M_engaged = false;
};

_Storage<_Stored_type> _M_payload即为实际存储的值,template _Storage为了保证可平凡析构的一致性同样写了个没有声明析构的可平凡析构版本的模板,和特化了一个带有空析构函数的版本:

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 _Up, bool = is_trivially_destructible_v<_Up>>
union _Storage
{
constexpr _Storage() noexcept : _M_empty() { }

template<typename... _Args>
constexpr
_Storage(in_place_t, _Args&&... __args)
: _M_value(std::forward<_Args>(__args)...)
{ }

template<typename _Vp, typename... _Args>
constexpr
_Storage(std::initializer_list<_Vp> __il, _Args&&... __args)
: _M_value(__il, std::forward<_Args>(__args)...)
{ }

#if __cplusplus >= 202002L
template<typename _Fn, typename _Arg>
constexpr
_Storage(_Optional_func<_Fn> __f, _Arg&& __arg)
: _M_value(std::__invoke(std::forward<_Fn>(__f._M_f),
std::forward<_Arg>(__arg)))
{ }
#endif

_Empty_byte _M_empty;
_Up _M_value;
};

template<typename _Up>
union _Storage<_Up, false>
{
// 与上面的版本实现一致,除定义了空的析构

// User-provided destructor is needed when _Up has non-trivial dtor.
_GLIBCXX20_CONSTEXPR ~_Storage() { }
};

从上面这部分代码可以看出optional实际存储在该union中。_Optional_payload_base中的_M_engaged表示实际是否存储值,由于字节对齐,一个optional<T>的大小可能将占用更大的内存。
optional<int32_t>对象为例,其结构为这样:

std::optional成员函数实现

构造函数

template < class U = T >constexpr optional( U&& value );为例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Converting constructors for engaged optionals.
template<typename _Up = _Tp,
_Requires<__not_self<_Up>, __not_tag<_Up>,
is_constructible<_Tp, _Up>,
is_convertible<_Up, _Tp>> = true>
constexpr
optional(_Up&& __t)
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
: _Base(std::in_place, std::forward<_Up>(__t)) { }

template<typename _Up = _Tp,
_Requires<__not_self<_Up>, __not_tag<_Up>,
is_constructible<_Tp, _Up>,
__not_<is_convertible<_Up, _Tp>>> = false>
explicit constexpr
optional(_Up&& __t)
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
: _Base(std::in_place, std::forward<_Up>(__t)) { }

_Base即是_Optional_base<_Tp>,通过in_place标签构造:

1
2
3
4
5
6
7
// Constructors for engaged optionals.
template<typename... _Args,
enable_if_t<is_constructible_v<_Tp, _Args...>, bool> = false>
constexpr explicit
_Optional_base(in_place_t, _Args&&... __args)
: _M_payload(in_place, std::forward<_Args>(__args)...)
{ }

构造_M_payload_Optional_payload使用基类_Optional_payload_base的构造:

1
2
3
4
5
6
template<typename... _Args>
constexpr
_Optional_payload_base(in_place_t __tag, _Args&&... __args)
: _M_payload(__tag, std::forward<_Args>(__args)...),
_M_engaged(true)
{ }

至最终_Storage构造:

1
2
3
4
5
template<typename... _Args>
constexpr
_Storage(in_place_t, _Args&&... __args)
: _M_value(std::forward<_Args>(__args)...)
{ }

以上即为optional其中一个构造的全过程。

reset

从上述构造函数的过程可以看出实际操作都是在payload类型里,这里也就不多描述外层接口,_Optional_payload_base_M_reset过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
constexpr void
_M_destroy() noexcept
{
_M_engaged = false;
_M_payload._M_value.~_Stored_type();
}

// _M_reset is a 'safe' operation with no precondition.
constexpr void
_M_reset() noexcept
{
if (this->_M_engaged)
_M_destroy();
}

emplace

1
2
3
4
5
6
7
8
9
10
11
//optional外层
template<typename... _Args>
_GLIBCXX20_CONSTEXPR
enable_if_t<is_constructible_v<_Tp, _Args...>, _Tp&>
emplace(_Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
{
this->_M_reset();
this->_M_construct(std::forward<_Args>(__args)...);
return this->_M_get();
}

省略中间代码,_Optional_payload_base中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//_Optional_payload_base
template<typename... _Args> constexpr void
_M_construct(_Args&&... __args)
noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>)
{
std::_Construct(std::__addressof(this->_M_payload._M_value),
std::forward<_Args>(__args)...);
this->_M_engaged = true;
}

constexpr _Tp&
_M_get() noexcept
{ return this->_M_payload._M_value; }

_M_construct_M_payload._M_value上原地构造对象后置_M_engagedtrue

swap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Swap.
_GLIBCXX20_CONSTEXPR void
swap(optional& __other)
noexcept(is_nothrow_move_constructible_v<_Tp>
&& is_nothrow_swappable_v<_Tp>)
{
using std::swap;

if (this->_M_is_engaged() && __other._M_is_engaged())
swap(this->_M_get(), __other._M_get());
else if (this->_M_is_engaged())
{
__other._M_construct(std::move(this->_M_get()));
this->_M_destruct();
}
else if (__other._M_is_engaged())
{
this->_M_construct(std::move(__other._M_get()));
__other._M_destruct();
}
}

逻辑也比较简单不赘述,这里再贴下_M_destruct实现:

1
2
3
4
// optional
constexpr void
_M_destruct() noexcept
{ static_cast<_Dp*>(this)->_M_payload._M_destroy(); }
1
2
3
4
5
6
7
//_Optional_payload_base
constexpr void
_M_destroy() noexcept
{
_M_engaged = false;
_M_payload._M_value.~_Stored_type();
}