C++进阶专栏: http://t.csdnimg.cn/j4mOf
相关系列文章
C++之std::tuple(二) : 揭秘底层实现原理
C++三剑客之std::any(一) : 使用
C++之std::tuple(一) : 使用精讲(全)
C++三剑客之std::variant(一) : 使用
C++三剑客之std::variant(二):深入剖析
深入理解可变参数(va_list、std::initializer_list和可变参数模版)
std::apply源码分析
目录
1.std::tuple存储设计
2.std::tuple构造
3.std::tuple_size
4.std::get<>访问值
5.operator=
6._Equals
7.总结
之前的章节中讲解std::tuple的使用和一些注意事项,接下来我们就以vs2019的std::tuple的实现来讲解它的底层实现原理。
1.std::tuple存储设计
std::tuple存储的递归写法基于这样的思想:一个包含N(N>0)个元素的元组可以存储为一个元素(第1个元素,或者说是列表的头部)加上一个包含N-1个元素的元组(尾部),而包含0个元素的元组是单独的特殊情况。下面看一下示例:
std::tuple<bool, int, double, std::string> a(true, 1, 3.0, "1112222");
因此,一个包含4个元素的元组a的递归构造如下:
1) 第一层递归。准备存储a
的第一个元素true,剩下的是 std:tuple<int,double,std::string>
对象。
2) 第二层递归。准备存储a
的第二个元素1,剩下的是 std::tuple<double,std::string>
对象。
3) 第三层递归。准备存储a
第三个元素3.0, 剩下的是 std::tuple<std::string>
对象。
4) 第四层递归。准备存储a
第四个元素"1112222",剩余的是std::tuple<>,遇到std::tuple<>递归结束。此时a
的才开始存储元素,并且存储元素是从"1112222"开始,然后递归开始返回。
5) 返回到第3步的递归,存储duble
类型的3.0。
6) 返回到第2步的递归,存储第int
类型的1。
7) 返回到第1步的递归,存储第bool
类型的true。
也就是说,std::tuple
的构造函数中,最后一个传入的元素最先构造,最先传入的元素最后一个构造,符合递归顺序,即入栈顺序。下面从源码角度,具体来看看 std::tuple
的实现。
template <class _This, class... _Rest>class tuple<_This, _Rest...> : private tuple<_Rest...> { // recursive tuple definitionpublic: using _This_type = _This; //当前元素 using _Mybase = tuple<_Rest...>; //余下的元素 //以下都是构造函数,罗列了部分 template <class _Tag, class _This2, class... _Rest2, enable_if_t<is_same_v<_Tag, _STD _Exact_args_t>, int> = 0> constexpr tuple(_Tag, _This2&& _This_arg, _Rest2&&... _Rest_arg) : _Mybase(_Exact_args_t{}, _STD forward<_Rest2>(_Rest_arg)...), _Myfirst(_STD forward<_This2>(_This_arg)) {} template <class _Tag, class _Tpl, size_t... _Indices, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>); template <class _Tag, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, _Tpl&& _Right) : tuple(_Unpack_tuple_t{}, _STD forward<_Tpl>(_Right), make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {} ... //获取余下的元素类,不带const constexpr _Mybase& _Get_rest() noexcept { // get reference to rest of elements return *this; } //获取余下的元素类,带const constexpr const _Mybase& _Get_rest() const noexcept { // get const reference to rest of elements return *this; } ... // template <size_t _Index, class... _Types> friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; template <size_t _Index, class... _Types> friend constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept; template <size_t _Index, class... _Types> friend constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept; template <size_t _Index, class... _Types> friend constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept; template <size_t _Index, class... _Types> friend constexpr auto&& _Tuple_get(tuple<_Types...>&& _Tuple) noexcept; template <class _Ty, class... _Types> friend constexpr _Ty& get(tuple<_Types...>& _Tuple) noexcept; template <class _Ty, class... _Types> friend constexpr const _Ty& get(const tuple<_Types...>& _Tuple) noexcept; template <class _Ty, class... _Types> friend constexpr _Ty&& get(tuple<_Types...>&& _Tuple) noexcept; template <class _Ty, class... _Types> friend constexpr const _Ty&& get(const tuple<_Types...>&& _Tuple) noexcept; //包装的当前的元素类, _Tuple_val<_This> _Myfirst; // the stored element };
从上面的代码看到,std::tuple的递归继承用到了private继承,说明各个元素都是独立的,互相没有关系。std::tuple的元素之间是一种组合的关系。另外一个是private继承可以造成empty base最优化,这对致力于“对象尺寸最小化”的程序开发者而言,可能很重要。
那么,tuple是如何存储其中的元素呢?
_Tuple_val<_This> _Myfirst; // the stored element
原来,它有个成员叫_Myfirst,它就是用来存储_This类型的变量的。你会看到_Myfirst的类型不是_This而是_Tuple_val<_This>,其实,_Tuple_val又是一个类模板,它的代码这里就不展开了,简而言之,它的作用是存储一个tuple中的变量。_Myfirst._Val才是真正的元素。从_Tuple_val的定义可以看出:
template <class _Ty>struct _Tuple_val { // stores each value in a tuple constexpr _Tuple_val() : _Val() {} template <class _Other> constexpr _Tuple_val(_Other&& _Arg) : _Val(_STD forward<_Other>(_Arg)) {} template <class _Alloc, class... _Other, enable_if_t<!uses_allocator_v<_Ty, _Alloc>, int> = 0> constexpr _Tuple_val(const _Alloc&, allocator_arg_t, _Other&&... _Arg) : _Val(_STD forward<_Other>(_Arg)...) {} template <class _Alloc, class... _Other, enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>, _STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>, int> = 0> constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg) : _Val(allocator_arg, _Al, _STD forward<_Other>(_Arg)...) {} template <class _Alloc, class... _Other, enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>, _STD negation<_STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>>, int> = 0> constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg) : _Val(_STD forward<_Other>(_Arg)..., _Al) {} _Ty _Val;};
在std::tuple类中定义_Myfirst的权限是public的,所以对外面而言是直接访问元素值的。
通过上面的分析,a中定义的类和类的继承关系图如下所示:
class std::tuple<>;class std::tuple<std::string>;class std::tuple<double, std::string>;class std::tuple<int, double, std::string>;class std::tuple<bool, int, double, std::string>;
内存的分布图如下:
2.std::tuple构造
tuple的构造函数就是初始化_Myfirst和_MyBase,当然,_MyBase也要进行么一个过程,直到tuple<>。
//分类构造template <class _Tag, class _This2, class... _Rest2, enable_if_t<is_same_v<_Tag, _STD _Exact_args_t>, int> = 0> constexpr tuple(_Tag, _This2&& _This_arg, _Rest2&&... _Rest_arg) : _Mybase(_Exact_args_t{}, _STD forward<_Rest2>(_Rest_arg)...), _Myfirst(_STD forward<_This2>(_This_arg)) {} template <class _Tag, class _Tpl, size_t... _Indices, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>); template <class _Tag, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, _Tpl&& _Right) : tuple(_Unpack_tuple_t{}, _STD forward<_Tpl>(_Right), make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {} template <class _Tag, class _Alloc, class _This2, class... _Rest2, enable_if_t<is_same_v<_Tag, _STD _Alloc_exact_args_t>, int> = 0> constexpr tuple(_Tag, const _Alloc& _Al, _This2&& _This_arg, _Rest2&&... _Rest_arg) : _Mybase(_Alloc_exact_args_t{}, _Al, _STD forward<_Rest2>(_Rest_arg)...), _Myfirst(_Al, allocator_arg, _STD forward<_This2>(_This_arg)) {} template <class _Tag, class _Alloc, class _Tpl, size_t... _Indices, enable_if_t<is_same_v<_Tag, _STD _Alloc_unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, const _Alloc& _Al, _Tpl&& _Right, index_sequence<_Indices...>); template <class _Tag, class _Alloc, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Alloc_unpack_tuple_t>, int> = 0> constexpr tuple(_Tag, const _Alloc& _Al, _Tpl&& _Right) : tuple(_Alloc_unpack_tuple_t{}, _Al, _STD forward<_Tpl>(_Right), make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}//根据入口参数的不同分派到不同的构造函数template <class _This2, class... _Rest2, enable_if_t<conjunction_v<_STD _Tuple_perfect_val<tuple, _This2, _Rest2...>, _STD _Tuple_constructible_val<tuple, _This2, _Rest2...>>, int> = 0> constexpr explicit(_Tuple_conditional_explicit_v<tuple, _This2, _Rest2...>) tuple(_This2&& _This_arg, _Rest2&&... _Rest_arg) noexcept(_Tuple_nothrow_constructible_v<tuple, _This2, _Rest2...>) // strengthened : tuple(_Exact_args_t{}, _STD forward<_This2>(_This_arg), _STD forward<_Rest2>(_Rest_arg)...) {}tuple(const tuple&) = default;tuple(tuple&&) = default;tuple& operator=(const volatile tuple&) = delete;
它还提供了默认拷贝构造函数和移动构造函数(移动语义是C++11中新增的特性,可以参考C++之std::move(移动语义)-CSDN博客)。其实,它还有很多构造函数,写起来挺热闹,无非就是用不同的方式为它赋初值,故省略。
上面的构造函数是根据_tag的不同被分派到不同的构造函数,它叫http://t.csdnimg.cn/j4mOf。源码中定义了如下的标签:
struct _Exact_args_t { explicit _Exact_args_t() = default;}; // tag type to disambiguate construction (from one arg per element)struct _Unpack_tuple_t { explicit _Unpack_tuple_t() = default;}; // tag type to disambiguate construction (from unpacking a tuple/pair)struct _Alloc_exact_args_t { explicit _Alloc_exact_args_t() = default;}; // tag type to disambiguate construction (from an allocator and one arg per element)struct _Alloc_unpack_tuple_t { explicit _Alloc_unpack_tuple_t() = default;}; // tag type to disambiguate construction (from an allocator and unpacking a tuple/pair)
3.std::tuple_size
先看一下源码:
template <class _Ty, _Ty _Val>struct integral_constant { static constexpr _Ty value = _Val; using value_type = _Ty; using type = integral_constant; constexpr operator value_type() const noexcept { return value; } _NODISCARD constexpr value_type operator()() const noexcept { return value; }};// TUPLE INTERFACE TO tupletemplate <class... _Types>struct tuple_size<tuple<_Types...>> : integral_constant<size_t, sizeof...(_Types)> {}; // size of tupletemplate <class _Ty1, class _Ty2>struct tuple_size<pair<_Ty1, _Ty2>> : integral_constant<size_t, 2> {}; // size of pair
std::integral_constant 包装特定类型的静态常量。它是 C++ 类型特征的基类。
std::tuple_size利用sizeof...求得可变参数的个数。
4.std::get<>访问值
先看一下tuple_element,tuple_element是获取tuple的元素,包括_Myfirst和_MyBase,源码:
//tuple<>template <size_t _Index>struct _MSVC_KNOWN_SEMANTICS tuple_element<_Index, tuple<>> { // enforce bounds checking static_assert(_Always_false<integral_constant<size_t, _Index>>, "tuple index out of bounds");};//tuple的_index == 0template <class _This, class... _Rest>struct _MSVC_KNOWN_SEMANTICS tuple_element<0, tuple<_This, _Rest...>> { // select first element using type = _This; // MSVC assumes the meaning of _Ttype; remove or rename, but do not change semantics using _Ttype = tuple<_This, _Rest...>;};//tuple的_index > 0template <size_t _Index, class _This, class... _Rest>struct _MSVC_KNOWN_SEMANTICS tuple_element<_Index, tuple<_This, _Rest...>> : tuple_element<_Index - 1, tuple<_Rest...>> {}; // recursive tuple_element definition//pairtemplate <size_t _Idx, class _Ty1, class _Ty2>struct _MSVC_KNOWN_SEMANTICS tuple_element<_Idx, pair<_Ty1, _Ty2>> { static_assert(_Idx < 2, "pair index out of bounds"); using type = conditional_t<_Idx == 0, _Ty1, _Ty2>;};template <size_t _Index, class _Tuple>using tuple_element_t = typename tuple_element<_Index, _Tuple>::type;
通过_Index获取tuple元素:
template <size_t _Index, class... _Types>_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept { using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype; return static_cast<_Ttype&>(_Tuple)._Myfirst._Val;}template <size_t _Index, class... _Types>_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept { using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype; return static_cast<const _Ttype&>(_Tuple)._Myfirst._Val;}template <size_t _Index, class... _Types>_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept { using _Ty = tuple_element_t<_Index, tuple<_Types...>>; using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype; return static_cast<_Ty&&>(static_cast<_Ttype&>(_Tuple)._Myfirst._Val);}template <size_t _Index, class... _Types>_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept { using _Ty = tuple_element_t<_Index, tuple<_Types...>>; using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype; return static_cast<const _Ty&&>(static_cast<const _Ttype&>(_Tuple)._Myfirst._Val);}
上述代码分析get<index>的流程:
1)通过_Index递归构造出类 tuple_element_t
2) 获取当前元素 _MyFirst.Val
通过_Ty获取tuple元素:
//辅助类template <class _Ty, class _Tuple>struct _Tuple_element {}; // backstop _Tuple_element definitiontemplate <class _This, class... _Rest>struct _Tuple_element<_This, tuple<_This, _Rest...>> { // select first element static_assert(!_Is_any_of_v<_This, _Rest...>, "duplicate type T in get<T>(tuple)"); using _Ttype = tuple<_This, _Rest...>;};template <class _Ty, class _This, class... _Rest>struct _Tuple_element<_Ty, tuple<_This, _Rest...>> { // recursive _Tuple_element definition using _Ttype = typename _Tuple_element<_Ty, tuple<_Rest...>>::_Ttype;};//通过_Ty gettemplate <class _Ty, class... _Types>_NODISCARD constexpr _Ty& get(tuple<_Types...>& _Tuple) noexcept { using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype; return static_cast<_Ttype&>(_Tuple)._Myfirst._Val;}template <class _Ty, class... _Types>_NODISCARD constexpr const _Ty& get(const tuple<_Types...>& _Tuple) noexcept { using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype; return static_cast<const _Ttype&>(_Tuple)._Myfirst._Val;}template <class _Ty, class... _Types>_NODISCARD constexpr _Ty&& get(tuple<_Types...>&& _Tuple) noexcept { using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype; return static_cast<_Ty&&>(static_cast<_Ttype&>(_Tuple)._Myfirst._Val);}template <class _Ty, class... _Types>_NODISCARD constexpr const _Ty&& get(const tuple<_Types...>&& _Tuple) noexcept { using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype; return static_cast<const _Ty&&>(static_cast<const _Ttype&>(_Tuple)._Myfirst._Val);}
上述代码分析get<_Ty>的流程:
1)通过_Ty递归构造出类 _Tuple_element
2) 获取当前元素 _MyFirst.Val
5.operator=
tuple重载了赋值符号(=),这样,tuple之间是可以赋值的。
tuple& operator=(const volatile tuple&) = delete; //以下是特殊情况是可以赋值的 template <class _Myself = tuple, class _This2 = _This, enable_if_t<conjunction_v<_STD _Is_copy_assignable_no_precondition_check<_This2>, _STD _Is_copy_assignable_no_precondition_check<_Rest>...>, int> = 0> _CONSTEXPR20 tuple& operator=(_Identity_t<const _Myself&> _Right) noexcept( conjunction_v<is_nothrow_copy_assignable<_This2>, is_nothrow_copy_assignable<_Rest>...>) /* strengthened */ { _Myfirst._Val = _Right._Myfirst._Val; _Get_rest() = _Right._Get_rest(); return *this; } template <class _Myself = tuple, class _This2 = _This, enable_if_t<conjunction_v<_STD _Is_move_assignable_no_precondition_check<_This2>, _STD _Is_move_assignable_no_precondition_check<_Rest>...>, int> = 0> _CONSTEXPR20 tuple& operator=(_Identity_t<_Myself&&> _Right) noexcept( conjunction_v<is_nothrow_move_assignable<_This2>, is_nothrow_move_assignable<_Rest>...>) { _Myfirst._Val = _STD forward<_This>(_Right._Myfirst._Val); _Get_rest() = _STD forward<_Mybase>(_Right._Get_rest()); return *this; } template <class... _Other, enable_if_t<conjunction_v<_STD negation<_STD is_same<tuple, _STD tuple<_Other...>>>, _STD _Tuple_assignable_val<tuple, const _Other&...>>, int> = 0> _CONSTEXPR20 tuple& operator=(const tuple<_Other...>& _Right) noexcept( _Tuple_nothrow_assignable_v<tuple, const _Other&...>) /* strengthened */ { _Myfirst._Val = _Right._Myfirst._Val; _Get_rest() = _Right._Get_rest(); return *this; } template <class... _Other, enable_if_t<conjunction_v<_STD negation<_STD is_same<tuple, _STD tuple<_Other...>>>, _STD _Tuple_assignable_val<tuple, _Other...>>, int> = 0> _CONSTEXPR20 tuple& operator=(tuple<_Other...>&& _Right) noexcept( _Tuple_nothrow_assignable_v<tuple, _Other...>) /* strengthened */ { _Myfirst._Val = _STD forward<typename tuple<_Other...>::_This_type>(_Right._Myfirst._Val); _Get_rest() = _STD forward<typename tuple<_Other...>::_Mybase>(_Right._Get_rest()); return *this; } template <class _First, class _Second, enable_if_t<_Tuple_assignable_v<tuple, const _First&, const _Second&>, int> = 0> _CONSTEXPR20 tuple& operator=(const pair<_First, _Second>& _Right) noexcept( _Tuple_nothrow_assignable_v<tuple, const _First&, const _Second&>) /* strengthened */ { _Myfirst._Val = _Right.first; _Get_rest()._Myfirst._Val = _Right.second; return *this; }
赋值符号返回左边的引用,这种行为和C++的内置类型是一致的。_Get_rest是tuple的成员函数,作用是把除了_Myfirst之外的那些元素拿出来。
6._Equals
//tuple内置函数 template <class... _Other> constexpr bool _Equals(const tuple<_Other...>& _Right) const { return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()); } //重载operator== template <class... _Types1, class... _Types2>_NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); return _Left._Equals(_Right);}
其中进行了静态断言,如果两个tuple的元素个数不相同,会引发一个编译时的错误。如果对应的类型不能用==进行比较,在模板特化时也会引发编译期的错误,例如,tuple<std::string, int>不能和tuple<int, char>比较,因为std::string和int是不能用==进行比较的。
7.总结
终于写完了,也是想了好久才写的,涉及到的知识点主要是模版推导。写的不好的地方,欢迎批评指正;如果觉得好,点个赞,收藏关注!