Std::optional 源码分析
背景:
std::optional 是c++17 新增的一个模版,用法也很简单,主要用于返回值是否设置了值,本文将从stl c++17的源码分析具体内部实现原理
用法:
| C++ #include #include #include #include // optional can be used as the return type of a factory that may fail std::optional create(bool b) { if (b) return "Godzilla"; return {}; } // std::nullopt can be used to create any (empty) std::optional auto create2(bool b) { return b ? std::optional{"Godzilla"} : std::nullopt; } // std::reference_wrapper may be used to return a reference auto create_ref(bool b) { static std::string value = "Godzilla"; return b ? std::optional>{value} : std::nullopt; } int main() { std::cout << "create(false) returned " << create(false).value_or("empty") << '\n'; // optional-returning factory functions are usable as conditions of while and if if (auto str = create2(true)) { std::cout << "create2(true) returned " << *str << '\n'; } if (auto str = create_ref(true)) { // using get() to access the reference_wrapper's value std::cout << "create_ref(true) returned " << str->get() << '\n'; str->get() = "Mothra"; std::cout << "modifying it changed it to " << str->get() << '\n'; } } |
Output
| C++ create(false) returned empty create2(true) returned Godzilla create_ref(true) returned Godzilla modifying it changed it to Mothra |
https://en.cppreference.com/w/cpp/utility/optional
源码分析
本文将从最简单的std::optional 进行,其内部实现还是挺复杂的
首先看下std::optional定义
std::optional
| C++ template class optional : private __optional_move_assign_base<_Tp> , private __optional_sfinae_ctor_base_t<_Tp> , private __optional_sfinae_assign_base_t<_Tp> { using __base = __optional_move_assign_base<_Tp>; public: using value_type = _Tp; } |
因为我们推到类型是int所以下边的__optional_sfinae_ctor_base_t 与 __optional_sfinae_assign_base_t 可以不用去关注,内部经过sfinae过后是空的实现
接下来看下__optional_move_assign_base的实现
__optional_move_assign_base
| C++ template is_trivially_destructible<_Tp>::value && is_trivially_move_constructible<_Tp>::value && is_trivially_move_assignable<_Tp>::value> struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> { using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base; };
template struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> { using value_type = _Tp; using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
_LIBCPP_INLINE_VISIBILITY __optional_move_assign_base() = default; _LIBCPP_INLINE_VISIBILITY __optional_move_assign_base(const __optional_move_assign_base& __opt) = default; _LIBCPP_INLINE_VISIBILITY __optional_move_assign_base(__optional_move_assign_base&&) = default; _LIBCPP_INLINE_VISIBILITY __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
_LIBCPP_INLINE_VISIBILITY __optional_move_assign_base& operator=(__optional_move_assign_base&& __opt) noexcept(is_nothrow_move_assignable_v && is_nothrow_move_constructible_v) { this->__assign_from(_VSTD::move(__opt)); return *this; } }; |
通过上边可以看到__optional_move_assign_base 有2个版本的实现一个是对于class有destructible,以及移动构造等,由于我们是推导的是int,所以第一个经过sfinae,推导不通过,会选择下边这个特化版本__optional_move_assign_base<_Tp, false>,这个strcut 继承__optional_copy_assign_base,所以我看下它的实现
__optional_copy_assign_base
| C++ template is_trivially_destructible<_Tp>::value && is_trivially_copy_constructible<_Tp>::value && is_trivially_copy_assignable<_Tp>::value> struct __optional_copy_assign_base : __optional_move_base<_Tp> { using __optional_move_base<_Tp>::__optional_move_base; };
template struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> { using __optional_move_base<_Tp>::__optional_move_base;
_LIBCPP_INLINE_VISIBILITY __optional_copy_assign_base() = default; _LIBCPP_INLINE_VISIBILITY __optional_copy_assign_base(const __optional_copy_assign_base&) = default; _LIBCPP_INLINE_VISIBILITY __optional_copy_assign_base(__optional_copy_assign_base&&) = default;
_LIBCPP_INLINE_VISIBILITY __optional_copy_assign_base& operator=(const __optional_copy_assign_base& __opt) { this->__assign_from(__opt); return *this; }
_LIBCPP_INLINE_VISIBILITY __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default; }; |
与上边类似__optional_copy_assign_base也有2个版本,这里就不详细解释了,我们推导类型是int所以选择下边的__optional_copy_assign_base<_Tp, false>特化版本,它又继承了__optional_move_base,这时候需要知道它的实现了
__optional_move_base
| C++ template ::value> struct __optional_move_base : __optional_copy_base<_Tp> { using __optional_copy_base<_Tp>::__optional_copy_base; };
template struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> { using value_type = _Tp; using __optional_copy_base<_Tp>::__optional_copy_base;
_LIBCPP_INLINE_VISIBILITY __optional_move_base() = default; _LIBCPP_INLINE_VISIBILITY __optional_move_base(const __optional_move_base&) = default;
_LIBCPP_INLINE_VISIBILITY __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v) { this->__construct_from(_VSTD::move(__opt)); }
_LIBCPP_INLINE_VISIBILITY __optional_move_base& operator=(const __optional_move_base&) = default; _LIBCPP_INLINE_VISIBILITY __optional_move_base& operator=(__optional_move_base&&) = default; }; |
与上边类似它也有2个版本,选择了特化版本struct __optional_move_base<_Tp, false>
struct __optional_move_base<_Tp, false> 又继承了__optional_copy_base<_Tp>,这里就去看下它的实现__optional_copy_base<_Tp>
__optional_copy_base<_Tp>
| C++ template ::value> struct __optional_copy_base : __optional_storage_base<_Tp> { using __optional_storage_base<_Tp>::__optional_storage_base; };
template struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> { using __optional_storage_base<_Tp>::__optional_storage_base;
_LIBCPP_INLINE_VISIBILITY __optional_copy_base() = default;
_LIBCPP_INLINE_VISIBILITY __optional_copy_base(const __optional_copy_base& __opt) { this->__construct_from(__opt); }
_LIBCPP_INLINE_VISIBILITY __optional_copy_base(__optional_copy_base&&) = default; _LIBCPP_INLINE_VISIBILITY __optional_copy_base& operator=(const __optional_copy_base&) = default; _LIBCPP_INLINE_VISIBILITY __optional_copy_base& operator=(__optional_copy_base&&) = default; }; |
同样它也有2个版本,最终推导选择__optional_copy_base<_Tp, false>,这个我们看下__optional_storage_base<_Tp>的定义
__optional_storage_base<_Tp>
| C++ template ::value> struct __optional_storage_base : __optional_destruct_base<_Tp> { using __base = __optional_destruct_base<_Tp>; using value_type = _Tp; using __base::__base; .............. };
// optional is currently required ill-formed, however it may to be in the // future. For this reason it has already been implemented to ensure we can // make the change in an ABI compatible manner. template struct __optional_storage_base<_Tp, true> { using value_type = _Tp; using __raw_type = remove_reference_t<_Tp>; __raw_type* __value_; ......... }; |
我们推导类型是int,is_reference<_Tp>::value 不是引用,所以它推导是上个struct __optional_storage_base : __optional_destruct_base<_Tp>,所以接下来看下 __optional_destruct_base<_Tp>的实现
__optional_destruct_base<_Tp>
| C++ template struct __optional_destruct_base<_Tp, false> { typedef _Tp value_type; static_assert(is_object_v, "instantiation of optional with a non-object type is undefined behavior"); union { char __null_state_; value_type __val_; }; bool __engaged_; } |
总结 sizeof(std::optional) = ?
通过上边源码分析,std::optional 最终数据是
| C++ union { char __null_state_; value_type __val_; }; bool __engaged_; |
也就是说value_type = int,所以sizeof(std::optional) == 8
std::optional 如果没设置去get会发生什么
很多人应该知道会抛异常,那我们就从源码来看