std::tuple,奇妙的模板递归
总算磨磨蹭蹭的看完了挪威的森林。。。。
上次既然提到了 tuple,就顺手。。。。 tuple stdlibc++v3, include/std/tuple
/// Primary class template, tuple
template<typename... _Elements>
class tuple : public _Tuple_impl<0, _Elements...>
{
typedef _Tuple_impl<0, _Elements...> _Inherited;
public:
constexpr tuple()
: _Inherited() { }
explicit
constexpr tuple(const _Elements&... __elements)
: _Inherited(__elements...) { }
template<typename... _UElements, typename = typename
enable_if<__and_<is_convertible<_UElements,
_Elements>...>::value>::type>
explicit
constexpr tuple(_UElements&&... __elements)
: _Inherited(std::forward<_UElements>(__elements)...) { }
constexpr tuple(const tuple&) = default;
constexpr tuple(tuple&&) = default;
template<typename... _UElements, typename = typename
enable_if<__and_<is_convertible<const _UElements&,
_Elements>...>::value>::type>
constexpr tuple(const tuple<_UElements...>& __in)
: _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
{ }
template<typename... _UElements, typename = typename
enable_if<__and_<is_convertible<_UElements,
_Elements>...>::value>::type>
constexpr tuple(tuple<_UElements...>&& __in)
: _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
class tuple 本身并没有很大信息量,提供构造函数,conversion 等。连个 member 都没有,关键部分都在 _Tuple_Impl 里面。
到下面,看到了一些奇奇怪怪的东西。
template<typename _Alloc>
tuple(allocator_arg_t __tag, const _Alloc& __a)
: _Inherited(__tag, __a) { }
我们暂时先不理 allocator_arg 这些东西,跟主线剧情无关。
后面就是 operator=(&) 和 operator(&&),同类型和不同类型。
tuple&
operator=(const tuple& __in)
{
static_cast<_Inherited&>(*this) = __in;
return *this;
}
tuple&
operator=(tuple&& __in)
noexcept(is_nothrow_move_assignable<_Inherited>::value)
{
static_cast<_Inherited&>(*this) = std::move(__in);
return *this;
}
template<typename... _UElements, typename = typename
enable_if<sizeof...(_UElements)
== sizeof...(_Elements)>::type>
tuple&
operator=(const tuple<_UElements...>& __in)
{
static_cast<_Inherited&>(*this) = __in;
return *this;
}
template<typename... _UElements, typename = typename
enable_if<sizeof...(_UElements)
== sizeof...(_Elements)>::type>
tuple&
operator=(tuple<_UElements...>&& __in)
{
static_cast<_Inherited&>(*this) = std::move(__in);
return *this;
}
注意,这里 enable_if 只要两边的 sizeof(types) 相同就可以。。。难道我们可以做一些奇怪的事情?比如说
tuple<我不是int但是跟int大小一样, double> c;
c = tuple<int, double>(1, 2.0);
不过显然是想多了,还有 base 的 operator=(&&) 挡着呢。
后面的 swap 调用了 base 的 swap。
void
swap(tuple& __in)
noexcept(noexcept(__in._M_swap(__in)))
{ _Inherited::_M_swap(__in); }
来看 _Tuple_Impl
template<std::size_t _Idx, typename... _Elements>
struct _Tuple_impl;
template<std::size_t _Idx>
struct _Tuple_impl<_Idx>
template<std::size_t _Idx, typename _Head, typename... _Tail>
struct _Tuple_impl<_Idx, _Head, _Tail...>
: public _Tuple_impl<_Idx + 1, _Tail...>,
private _Head_base<_Idx, _Head, __empty_not_final<_Head>::value>
是在用模板做递归继承的勾当,而 _Head_base 就是当前的 type 储存的地方。tuple 是继承 _Idx == 0 时的版本
class tuple : public _Tuple_impl<0, _Elements...>
我们来理一下递推关系吧。假设有这样一个 tuple<int, char>,那么 tuple : _Tuple_impl<0, int, char, bool>。 _Tuple_impl 发生模板匹配的时候,_Head 匹配到了 int,而 typename… _Tail 匹配到了 char, bool (可变模板嘛)。而根据继承的递推关系,我们有 _Tuple_impl<0, int, ….> : _Tuple_impl<1, ….. > 。省略号的部分继续匹配 _Tuple_impl,此时 char 被 _Head 匹配,而 _Tail 中只剩 bool 一个。。。匹配到最后,Tail 中已经没有类型了,我们到了 _Tuple_Impl<_Idx> ,也就是整个递推的 base,结束。
而这些 int, char, bool 则都交给了 _Head_base 来管理,我们来看一下。
template<std::size_t _Idx, typename _Head, bool _IsEmptyNotFinal>
struct _Head_base;
template<std::size_t _Idx, typename _Head>
struct _Head_base<_Idx, _Head, true>
: public _Head
template<std::size_t _Idx, typename _Head>
struct _Head_base<_Idx, _Head, false>
在做跟 compressed_pair 类似的勾当,还是想尽量做空类继承优化。并没有太多细节,略过一切 alloc 有关的东西。
回到 _tuple_impl,我们顺手看一下 swap
protected:
void
_M_swap(_Tuple_impl& __in)
noexcept(noexcept(swap(std::declval<_Head&>(),
std::declval<_Head&>()))
&& noexcept(_M_tail(__in)._M_swap(_M_tail(__in))))
{
using std::swap;
swap(_M_head(*this), _M_head(__in));
_Inherited::_M_swap(_M_tail(__in));
}
也不过如此,分别交换 head 和 base。
然后 _Tuple_impl 里面就是各种炫酷的 operator=(&&) operator=(&) 了,就不细说了。
关于 tuple,当然后面还有内容。
template<>
class tuple<>
{
public:
void swap(tuple&) noexcept { /* no-op */ }
};
对空 tuple 的显示特化。(库里面总是会做一些你想不到的奇怪事情)
template<typename _T1, typename _T2>
class tuple<_T1, _T2> : public _Tuple_impl<0, _T1, _T2>
template<typename _U1, typename _U2, typename = typename
enable_if<__and_<is_convertible<const _U1&, _T1>,
is_convertible<const _U2&, _T2>>::value>::type>
constexpr tuple(const pair<_U1, _U2>& __in)
: _Inherited(__in.first, __in.second) { }
template<typename _U1, typename _U2, typename = typename
enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>>::value>::type>
constexpr tuple(pair<_U1, _U2>&& __in)
: _Inherited(std::forward<_U1>(__in.first),
std::forward<_U2>(__in.second)) { }
对两个元素的 tuple 提供从 pair 构造的方法。
对于 tuple 本身,基本完结了。不过 tuple 周边还有东西哟。
std::get 拿到 tuple 中的元素,std::tuple_element 拿到 tuple 中的类型,tuple_size 等等。 想一想,其实这些东西原理应该都差不多,搞的定一个,其他的基本同理了。随便 yy 一下,直接跟 tuple 构造一样做模板递归不就好了~
我们来看最喜闻乐见的 std::get 先。
显示 get<size_t>
template<std::size_t __i, typename _Head, typename... _Tail>
constexpr typename __add_ref<_Head>::type
__get_helper(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
{ return _Tuple_impl<__i, _Head, _Tail...>::_M_head(__t); }
template<std::size_t __i, typename _Head, typename... _Tail>
constexpr typename __add_c_ref<_Head>::type
__get_helper(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
{ return _Tuple_impl<__i, _Head, _Tail...>::_M_head(__t); }
// Return a reference (const reference, rvalue reference) to the ith element
// of a tuple. Any const or non-const ref elements are returned with their
// original type.
template<std::size_t __i, typename... _Elements>
constexpr typename __add_ref<
typename tuple_element<__i, tuple<_Elements...>>::type
>::type
get(tuple<_Elements...>& __t) noexcept
{ return std::__get_helper<__i>(__t); }
template<std::size_t __i, typename... _Elements>
constexpr typename __add_c_ref<
typename tuple_element<__i, tuple<_Elements...>>::type
>::type
get(const tuple<_Elements...>& __t) noexcept
{ return std::__get_helper<__i>(__t); }
template<std::size_t __i, typename... _Elements>
constexpr typename __add_r_ref<
typename tuple_element<__i, tuple<_Elements...>>::type
>::type
get(tuple<_Elements...>&& __t) noexcept
{ return std::forward<typename tuple_element<__i,
tuple<_Elements...>>::type&&>(get<__i>(__t)); }
然后是 get<type>
#if __cplusplus > 201103L
template<typename _Head, size_t __i, typename... _Tail>
constexpr typename __add_ref<_Head>::type
__get_helper2(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
{ return _Tuple_impl<__i, _Head, _Tail...>::_M_head(__t); }
template<typename _Head, size_t __i, typename... _Tail>
constexpr typename __add_c_ref<_Head>::type
__get_helper2(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
{ return _Tuple_impl<__i, _Head, _Tail...>::_M_head(__t); }
template <typename _Tp, typename... _Types>
constexpr _Tp&
get(tuple<_Types...>& __t) noexcept
{ return std::__get_helper2<_Tp>(__t); }
template <typename _Tp, typename... _Types>
constexpr _Tp&&
get(tuple<_Types...>&& __t) noexcept
{ return std::move(std::__get_helper2<_Tp>(__t)); }
template <typename _Tp, typename... _Types>
constexpr const _Tp&
get(const tuple<_Types...>& __t) noexcept
{ return std::__get_helper2<_Tp>(__t); }
#endif
get<type> 的时候如果你一个 tuple 里面有两个 这个 type 的时候,就会出现两个可以匹配的 _Tuple_Impl,编译挂掉。
并不复杂,直接从 _Tuple_impl::_M_head 拿了。
typedef _Head_base<_Idx, _Head, __empty_not_final<_Head>::value> _Base;
static constexpr _Head&
_M_head(_Tuple_impl& __t) noexcept { return _Base::_M_head(__t); }
static constexpr const _Head&
_M_head(const _Tuple_impl& __t) noexcept { return _Base::_M_head(__t); }
下面还有好玩的各种比较 operator
template<typename... _TElements, typename... _UElements>
constexpr bool
operator!=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__t == __u); }
template<typename... _TElements, typename... _UElements>
constexpr bool
operator>(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return __u < __t; }
template<typename... _TElements, typename... _UElements>
constexpr bool
operator<=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__u < __t); }
template<typename... _TElements, typename... _UElements>
constexpr bool
operator>=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__t < __u); }
都是基于 operator< 和 operator= 定义的。
template<typename... _TElements, typename... _UElements>
constexpr bool
operator==(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{
typedef tuple<_TElements...> _Tp;
typedef tuple<_UElements...> _Up;
return bool(__tuple_compare<tuple_size<_Tp>::value - tuple_size<_Up>::value,
0, tuple_size<_Tp>::value, _Tp, _Up>::__eq(__t, __u));
}
template<typename... _TElements, typename... _UElements>
constexpr bool
operator<(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{
typedef tuple<_TElements...> _Tp;
typedef tuple<_UElements...> _Up;
return bool(__tuple_compare<tuple_size<_Tp>::value - tuple_size<_Up>::value,
0, tuple_size<_Tp>::value, _Tp, _Up>::__less(__t, __u));
}
operator== 和 operator< 借助 __tuple_compare 来做比较。
// This class helps construct the various comparison operations on tuples
template<std::size_t __check_equal_size, std::size_t __i, std::size_t __j,
typename _Tp, typename _Up>
struct __tuple_compare;
template<std::size_t __i, std::size_t __j, typename _Tp, typename _Up>
struct __tuple_compare<0, __i, __j, _Tp, _Up>
{
static constexpr bool
__eq(const _Tp& __t, const _Up& __u)
{
return (get<__i>(__t) == get<__i>(__u) &&
__tuple_compare<0, __i + 1, __j, _Tp, _Up>::__eq(__t, __u));
}
static constexpr bool
__less(const _Tp& __t, const _Up& __u)
{
return ((get<__i>(__t) < get<__i>(__u))
|| !(get<__i>(__u) < get<__i>(__t)) &&
__tuple_compare<0, __i + 1, __j, _Tp, _Up>::__less(__t, __u));
}
};
template<std::size_t __i, typename _Tp, typename _Up>
struct __tuple_compare<0, __i, __i, _Tp, _Up>
{
static constexpr bool
__eq(const _Tp&, const _Up&) { return true; }
static constexpr bool
__less(const _Tp&, const _Up&) { return false; }
};
__tuple_compare 先通过 __check_equal_size 进行判断,两个 tuple 是不是有同样多的元素。__tuple_compare 只对 __check_equal_size == 0 的情况作了特化,不一样就会挂。 [b]而 __i 作为从 0 开始的步进量, __j 则是 tupe_size,__i 从 0 到 tuple_size 开始,逐个比较 tuple 中的元素。而如果 __i 到了 __j ,就到了 __tuple_compare 第二个特化中去了~~ 一看便知。相当于编译期生成 loop 喔。[/b]
顺便路过 make_tuple 和 forward_as_tuple
template<typename... _Elements>
constexpr tuple<typename __decay_and_strip<_Elements>::__type...>
make_tuple(_Elements&&... __args)
{
typedef tuple<typename __decay_and_strip<_Elements>::__type...>
__result_type;
return __result_type(std::forward<_Elements>(__args)...);
}
template<typename... _Elements>
tuple<_Elements&&...>
forward_as_tuple(_Elements&&... __args) noexcept
{ return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); }
对了,我比较关心的还有 std::tie 和 std::ignore.
/// tie
template<typename... _Elements>
inline tuple<_Elements&...>
tie(_Elements&... __args) noexcept
{ return tuple<_Elements&...>(__args...); }
哈哈,就是引用,会不会有些失望。
// A class (and instance) which can be used in 'tie' when an element
// of a tuple is not required
struct _Swallow_assign
{
template<class _Tp>
const _Swallow_assign&
operator=(const _Tp&) const
{ return *this; }
};
const _Swallow_assign ignore{};
这就是 ignore !! 恍然大悟。真是。。。给跪下了啊
总结一下~~
- 编译期模板的递归真是强大啊,可以生成递归类型,还有递归代码逻辑(operator 比较那个)
- 另外库里面各种 forward && noexcept 应该找时间专门研究一下~~~ 还有让人蛋碎的引用塌陷
折叠