您的位置:首页 > 其它

tuple

2014-02-25 14:04 375 查看

返回值的捆绑

pair

使用std::pair<>来定义函数的返回值类型(顾名思义,std::pair<>可以将两个值凑成一对),像这样:

std::pair<int,int> DevideInts(int n,int d)
{
    return std::pair<int,int>(n/d,n%d);
}

缺点

只能提供两个返回值的捆绑,如果现在需要返回三个int呢

方法一:自己定义一个结构

定义class/struct,保存三个乃至更多值

缺点:

随着不同函数的需要你可能需要定义各种不同的类似这样的结构

方法二:Tuple(代替struct)

能够用来保存任意型别的任意多个变量的类

/tuple<>目前能够支持多达10个模板参数
boost::tuple<int,int,int>
someFunc();

Tuple原理

目标:

tuple中的数据成员的个数应该具有某种动态特性 [利用了某种递归的概念];

tuple 必须提供某种途径以获取它内部保存的数值[通过某种编译期的递归],注意:当你需要获取第N个数据时,你所提供的N必须是编译期可计算出的常量。

boost::tuple源码剖析

//请记住它,后面我们将一直围绕这个例子
boost::tuple<int,long,bool>myTuple(10,10,true);
 

tuple的声明

所有模板参数都有缺省值null_type:

template < class T0
= null_type, class T1 = null_type, class T2
= null_type,
class T3 = null_type, class T4
= null_type, class T5 = null_type,
class T6 = null_type, class T7
= null_type, class T8 = null_type,
class T9 = null_type> // null_type是个空类
class tuple;  // 注意这个声明的所有模板参数都有缺省值null_type

boost::tuple的定义

tuple只是将参数转交给基类处理:

 template <class T0, class T1, class T2, class T3, class T4,
               class T5, class T6, class T7, class T8, class T9>
    class tuple :
      public detail::map_tuple_to_cons<T0,
T1, T2,T3, T4,

T5, T6, T7, T8,T9>::type
{
// tuple的定义体十分简单,其中是若干构造函数(将参数转交给基类)和模板赋值操作符
  …
}; // 为了凸显重点,以下先讲tuple的基类

tuple的基类:map_tuple_to_cons<>

例子:

tuple<int,long,bool>myTuple(10,10,true);相当于:
tuple<int,long,bool,
null_type,null_type,null_type,null_type,
null_type,null_type,null_type
myTuple(10,10,true);
T0,T1,...,T9分别是int,long,bool,null_type,.....null_type

基类定义

step1:递归定义:

template <class T0, class T1, class T2, class T3, class T4,
               class T5, class T6, class T7, class T8, class T9>
    struct map_tuple_to_cons
    {
    // cons<>是数据的容器,也是所有奥秘所在
   typedef cons<
T0, //第一个参数T0被孤立出来
         typename map_tuple_to_cons<//剩下的模板参数后跟一个null_type进入下一轮
T1, T2, T3, T4, T5,T6, T7, T8, T9, null_type
>::type
       > type;
};
step2模板特化:终止某种递归式
template <>  //这个特化版本是终止某种递归式的自包含定义的关键,后面你会明白
struct map_tuple_to_cons<null_type,null_type, null_type, null_type,
null_type,null_type, null_type, null_type,
null_type,null_type>
   {
      typedef null_type type;
};
例子分析
上面已经知道T0,T1,...,T9被推导为int,long,bool,null_type,...,null_type
tuple的基类:
detail::map_tuple_to_cons<T0,T1, T2, T3, T4, T5, T6, T7, T8, T9>::type
被推导为
map_tuple_to_cons<int,long,bool,null_type,...,null_type>::type
根据map_tuple_to_cons的定义,其实就是:
 consint,
typename map_tuple_to_cons<long,bool,null_type,...,null_type>::type

其中的
typename map_tuple_to_cons<long,bool,null_type,...,null_type>::type被推导为
cons<long,typename map_tuple_to_cons<bool,null_type,...,null_type>::type>
基类的定义的形式被推导成【原来的基类map_tuple_to_cons<int,long,bool,null_type,...,null_type>::type
】:
cons<int,
cons<long,
typename map_tuple_to_cons<bool,null_type,...,null_type>::type


最终推导成递归嵌套的模式:
cons<int,
cons<long,
cons<bool,
typename map_tuple_to_cons<null_type,...,null_type>::type



map_tuple_to_cons<>准备了一个特化版本来终止递归
使用模板特化中的typedef将
typename map_tuple_to_cons<null_type,...,null_type>::type
推导为null_type,得到最终的形式:
cons<int,cons<long,cons<bool,null_type>
>> 
// 这实际上只是为int,long,bool各分配一份空间
这就是tuple<int,long,bool>的基类.
类似的:
如果tuple的形式为tuple<int,long,bool,double>,则其基类为:
cons<int,cons<long,cons<bool,cons<double,null_type>>
> >。
用户提供的模板参数个数的变化(反映用户需要保存的数据的个数)导致cons<>容器的嵌套层数的变化,进而导致tuple的底层内存的分配量也作相应变化。
总结:
map_tuple_to_cons<>以一种递归的方式不断将它的第一个模板参数割裂出来,并使tuple的基类呈现像这样的形式:
cons<T0,cons<T1,cons<T2,cons<T3,... ... > >> >
这种递归当map_tuple_to_cons<>的模板参数都为null_type时才恰好停止,。
1,             map_tuple_to_cons<>不断将第一个模板参数取出,并将剩余的参数在尾部添一个null_type再传递下去。
2,    当用户给出的模板参数全部被分离出来时,map_tuple_to_cons<>所接受的参数就全部都是null_type了,于是使用其特化版本,其中将内嵌型别type typedef为null_type。从而结束这场递归。

基类中容器cons<>的定义

template <class HT, class TT>
   struct cons {
     typedef HT head_type; // 这是个用户提供的型别
     typedef TT tail_type;   // 这通常是个cons<>的具现体
                             // 以上两个typedef很重要,并非可有可无
     typedef
       typename detail::wrap_non_storeable_type<head_type>::type
stored_head_type;
stored_head_type head; // 这是其中第一个数据成员
tail_type tail;            // 第二个数据成员
 ...                        // 其成员函数将在后面解释,此处先略去
};
// cons<>还有一个偏特化版本:
template <class HT>
   struct cons<HT,null_type> {
typedef HT head_type;
     typedef null_typetail_type;
     typedef cons<HT,null_type> self_type;
     typedef typename
       detail::wrap_non_storeable_type<head_type>::typestored_head_type;
 
stored_head_type head;
// 注意,不像上面的主模板,这里没有tail成员
     ... // 成员函数将在后面解释
};
根据cons<>的定义显示它有两个数据成员:
stored_head_type head; // 这是其中第一个数据成员
 tail_type tail;            // 第二个数据成员
对于第一个数据成员的型别stored_head_type,它被typedef为:
detail::wrap_non_storeable_type<head_type>::type
// 而head_type又被typedef为HT
其实它只是用来侦测你是否使用了void型别和函数类型【使用二者时利用模板特化】。
现在回顾我们的示例代码:tuple<int,long,bool>
myTuple;tuple<int,long,bool>的基类为:
    cons<int,cons<long,cons<bool,null_type>
> >
 所以,最外层的cons<>的模板参数被推导为
typename HT=int,typename TT= cons<long,cons<bool,null_type>
>
这样,tuple<int,long,bool>的基类cons<int,cons<long,cons<bool,null_type>>
>其实只拥有两个成员:
int head;
cons<long,cons<bool,null_type>
> tail; // 注意这又是一个cons<>对象
tail成员又是cons<>的一个对象,不同的是tail的型别不同了——具现化cons<>的模板参数不同。可想而知,tail内部包含两个成员:
long head;
cons<bool,null_type>
tail;
值得注意的是,第二个tail的型别匹配的是cons<>的偏特化版本,其中只有一个数据成员:
bool head;
整个基类的内存布局其实就是cons<>的三重嵌套。
三个head数据成员就是需要分配内存的主体
      


嵌套的结构,这种布局正像一种玩具——开始是一个盒子,揭开盒子其内部又是个更小的盒子,再揭,还是盒子...
嵌套的重数可以由用户给出的模板参数个数来控制。
特别注意:如果重数为N重,则只有N个head占用内存
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: