前言
对之前所学进行检验。从源码中学习如何使用模板。
定义以及使用
-
integer_sequence:判断是否是整数类型,不是则编译失败(利用静态断言),是则保存自身类型和传入的的整形类型,以及保存可变参数的长度。
-
index_sequence_for:生成指定大小的integer_sequence类型。例如
index_sequence_for<3> —> integer_sequence<size_t, 0, 1, 2, 3>
一、integer_sequence源码
老规矩先贴源码。(说明一下,这个是我找的其他版本的源码,和之前不是一个版本,但是基本实现类似。之前那个版本,有部分源码找不到)
// TEMPLATE STRUCT integer_sequence
template<class _Ty,
_Ty... _Vals>
struct integer_sequence
{ // sequence of integer parameters
static_assert(is_integral<_Ty>::value,
"integer_sequence<T, I...> requires T to be an integral type.");
typedef integer_sequence<_Ty, _Vals...> type;
typedef _Ty value_type;
static _CONST_FUN size_t size() _NOEXCEPT
{ // get length of parameter list
return (sizeof...(_Vals));
}
};
// ALIAS TEMPLATE make_integer_sequence
template<bool _Negative,
bool _Zero,
class _Int_con,
class _Int_seq>
struct _Make_seq
{ // explodes gracefully below 0
static_assert(!_Negative,
"make_integer_sequence<T, N> requires N to be non-negative.");
};
template<class _Ty,
_Ty... _Vals>
struct _Make_seq<false, true,
integral_constant<_Ty, 0>,
integer_sequence<_Ty, _Vals...> >
: integer_sequence<_Ty, _Vals...>
{ // ends recursion at 0
};
template<class _Ty,
_Ty _Ix,
_Ty... _Vals>
struct _Make_seq<false, false,
integral_constant<_Ty, _Ix>,
integer_sequence<_Ty, _Vals...> >
: _Make_seq<false, _Ix == 1,
integral_constant<_Ty, _Ix - 1>,
integer_sequence<_Ty, _Ix - 1, _Vals...> >
{ // counts down to 0
};
template<class _Ty,
_Ty _Size>
using make_integer_sequence = typename _Make_seq<_Size < 0, _Size == 0,
integral_constant<_Ty, _Size>, integer_sequence<_Ty> >::type;
template<size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;
template<size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;
template<class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
之前源码__make_integer_seq这个模板的实现没了。没办法我又找了个老版本的源码。但是对比发现已知实现是一样的。
template <class _Ty, _Ty... _Vals>
struct integer_sequence { // sequence of integer parameters
static_assert(is_integral_v<_Ty>, "integer_sequence<T, I...> requires T to be an integral type.");
using value_type = _Ty;
_NODISCARD static constexpr size_t size() noexcept {
return sizeof...(_Vals);
}
};
template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;
template <size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;
template <size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;
template <class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
二、使用步骤
1.全部解析
// TEMPLATE STRUCT integer_sequence
template<class _Ty,
_Ty... _Vals>
struct integer_sequence
{ // sequence of integer parameters
static_assert(is_integral<_Ty>::value,
"integer_sequence<T, I...> requires T to be an integral type.");
//is_integral<_Ty>判断是不是整形,继承的的bool类型,is_integral<_Ty>::type得到false或者true,是true断言不会起作用,是false则直接报错。
typedef integer_sequence<_Ty, _Vals...> type;
//对自身的类型重新定义。保存所有变量及其类型
typedef _Ty value_type;
//保存类型
static _CONST_FUN size_t size() _NOEXCEPT
{ // get length of parameter list
return (sizeof...(_Vals));
}
};
//size()函数可以获取可变变量的个数。
//可以从后往前看。后边有调用。
//定义一个模板基类。对特殊情况进行过滤
// ALIAS TEMPLATE make_integer_sequence
template<bool _Negative,
bool _Zero,
class _Int_con,
class _Int_seq>
struct _Make_seq
{ // explodes gracefully below 0
static_assert(!_Negative,
"make_integer_sequence<T, N> requires N to be non-negative.");
//第一个参数不能是true,是则直接断言报错。
};
//特化上述基类。当第一个参数为false,第二个参数为true就会被调用,继承integer_sequence
template<class _Ty,
_Ty... _Vals>
struct _Make_seq<false, true,
integral_constant<_Ty, 0>,
integer_sequence<_Ty, _Vals...> >
: integer_sequence<_Ty, _Vals...>
{ // ends recursion at 0
};
//对上述偏特化的模板再次偏特化。当第二个参数为flase时调用。
template<class _Ty,
_Ty _Ix,
_Ty... _Vals>
struct _Make_seq<false, false,
integral_constant<_Ty, _Ix>,
integer_sequence<_Ty, _Vals...> >
: _Make_seq<false, _Ix == 1,
integral_constant<_Ty, _Ix - 1>,
integer_sequence<_Ty, _Ix - 1, _Vals...> >
//这步就是关键,利用继承把参数变多,例如输入3最后就会变成1,2,3
//_Make_seq<false, _Ix == 1,
//看第二个参数是不是得1,如果是则调用上述第二个参数为true的时候。将所有参数送入integer_sequence
//不是得话继续调用自身。
//integral_constant<_Ty, _Ix - 1>,integer_sequence<_Ty, _Ix - 1, _Vals...> >
//integer_sequence<_Ty, _Ix - 1, _Vals...>将可变参数的个数+1.(_Ix - 1)
//这个就是3会变成1,2,3的原因。
//integer_sequence<size_t, 3-1, 3>
//integer_sequence<size_t, 2-1, 2, 3>
//integer_sequence<size_t, 1-1, 1, 2, 3>后 1==1第二个参数为true
//因此会调用struct _Make_seq<false, true,integral_constant<_Ty, 0>,
//在这就会将<size_t, 0, 1, 2, 3>可变参数送入integer_sequence<_Ty, _Vals...>
//最后就会返回integer_sequence<size_t, 0, 1, 2, 3>
//有个疑问点integral_constant没起作用啊。
{ // counts down to 0
};
//对_Make_seq使用安全过滤,判断数字是不是小于0和等于0,两个边界条件
//自动创造。制造指定个数的integer_sequence的类型,例如输入3最后就会变成integer_sequence<_Ty, 0, 1, 2, 3>
template<class _Ty,
_Ty _Size>
using make_integer_sequence = typename _Make_seq<_Size < 0, _Size == 0,
integral_constant<_Ty, _Size>, integer_sequence<_Ty> >::type;
//自己手动创造integer_sequence。
template<size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;
//创造size_t类型的integer_sequence,例如输入3最后就会变成integer_sequence<size_t, 0, 1, 2, 3>
template<size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;
//随便输入,可变参数有多少个就会创建多少个integer_sequence<size_t, 0...sizeof...(_Types)>
//就是一步一步调用的
template<class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
2.重点解析
1. 递归调用自身的模板思想
看懂这个就对模板如何递归处理可变参数有了了解了。
template<class _Ty,
_Ty... _Vals>
struct _Make_seq<false, true,
integral_constant<_Ty, 0>,
integer_sequence<_Ty, _Vals...> >
: integer_sequence<_Ty, _Vals...>
{ // ends recursion at 0
};
template<class _Ty,
_Ty _Ix,
_Ty... _Vals>
struct _Make_seq<false, false,
integral_constant<_Ty, _Ix>,
integer_sequence<_Ty, _Vals...> >
: _Make_seq<false, _Ix == 1,
integral_constant<_Ty, _Ix - 1>,
integer_sequence<_Ty, _Ix - 1, _Vals...> >
integer_sequence<_Ty, _Ix - 1, _Vals…>
integer_sequence<_Ty, _Ix - 1, _Vals…>将可变参数的个数+1.(_Ix - 1)
这个就是3会变成1,2,3的原因。
下面模拟一下调用过程
integer_sequence<size_t, 3-1, 3>
integer_sequence<size_t, 2-1, 2, 3>
integer_sequence<size_t, 1-1, 1, 2, 3>后 1==1第二个参数为true
因此会调用struct _Make_seq<false, true,integral_constant<_Ty, 0>
在这就会将<size_t, 0, 1, 2, 3>可变参数送入integer_sequence<_Ty, _Vals…>
最后就会返回integer_sequence<size_t, 0, 1, 2, 3>
2. integral_constant存在的必要性。
你在看上述推导过程的时候会发现integral_constant<_Ty, _Ix>,一直存在但是没被使用,那他可以被去掉吗,带着这个疑问我们进行测试
demo
#include <iostream>
#include <xtr1common>
using namespace std;
namespace xx {
template<class _Ty,
_Ty... _Vals>
struct integer_sequence
{ // sequence of integer parameters
static_assert(is_integral<_Ty>::value,
"integer_sequence<T, I...> requires T to be an integral type.");
typedef integer_sequence<_Ty, _Vals...> type;
typedef _Ty value_type;
static const size_t size()
{ // get length of parameter list
return (sizeof...(_Vals));
}
};
// ALIAS TEMPLATE make_integer_sequence
template<bool _Negative,
bool _Zero,
class _Int_seq>
struct _Make_seq
{ // explodes gracefully below 0
static_assert(!_Negative,
"make_integer_sequence<T, N> requires N to be non-negative.");
};
template<class _Ty,
_Ty... _Vals>
struct _Make_seq<false, true,
integer_sequence<_Ty, _Vals...> >
: integer_sequence<_Ty, _Vals...>
{ // ends recursion at 0
};
template<class _Ty,
_Ty _Ix,
_Ty... _Vals>
struct _Make_seq<false, false,
integer_sequence<_Ty, _Vals...> >
: _Make_seq<false, _Ix == 1,
integer_sequence<_Ty, _Ix - 1, _Vals...> >
{ // counts down to 0
};
template<class _Ty,
_Ty _Size>
using make_integer_sequence = typename _Make_seq < _Size < 0, _Size == 0,
integer_sequence<_Ty> >::type;
template<size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;
template<size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;
template<class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
}
int main(void)
{
index_sequence_for<int, double, float> f;
cout << typeid(decltype(f)).name() << endl;
return 0;
}
从编译结果可以看出如果把integral_constant去掉,没法推导其模板参数。integer_sequence<_Ty, _Ix - 1, _Vals…>这里的Ix-1,被归入_Vals…这里。因此没法显示推导出Ix,因此才需要利用integral_constant<_Ty, _Ix - 1>显示调用一次。
总结
通过源码学习。学习到两点;
- 如何利用继承的方式去增加可变参数。
- template<class _Ty, _Ty _Ix, _Ty… _Vals>这种类型时需要显示调用_Ty _Ix这种类型时,利用integral_constant可以显示调用。
注:说一下,这里我迷惑的地方。我一直以为integral_constant<_Ty, _Ix - 1>是不起作用的。以为integer_sequence<_Ty, _Ix - 1, _Vals…>再下次递归的时候会自动将第一个可变参数提取出来。template<_Ty, __Ix - 1,_Vals…>但是最后结果看就是不能。但是仔细分析也是对的。之前也有个递归的例子。
template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};
template <class _False, class _Next, class… _Rest>
_Disjunction<false, _False, _Next, _Rest…>
_Disjunction<_Next::value, _Next, _Rest…>
他确实要是想用_Next,就需要特化指出来。
struct _Make_seq<false, false,integer_sequence<_Ty, _Vals…> >我这种想法就不对。因为我想用Ix,但是我没有显示指出来。其实本质就是将那个值特化出去就可以。
但是因为无法指定某一种类型,你把他的类型没法指定,因此才要借助integral_constant
我只是想用一下Ix这个值,但是Ix没法指定某一种类型,因此利用integral_constant中转了一下。
3. 在可变参数中,要想使用可变参数第一个需要特化出来,显示调用。才可以使用。