type_traits-integer_sequence解析(继承方式添加可变参数)

前言

对之前所学进行检验。从源码中学习如何使用模板。

定义以及使用

  1. integer_sequence:判断是否是整数类型,不是则编译失败(利用静态断言),是则保存自身类型和传入的的整形类型,以及保存可变参数的长度。

  2. 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>显示调用一次。

总结

通过源码学习。学习到两点;

  1. 如何利用继承的方式去增加可变参数。
  2. 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. 在可变参数中,要想使用可变参数第一个需要特化出来,显示调用。才可以使用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值