浅析C++标准库元组(tuple)源码

一、什么是元组

元组不是什么新鲜东西,在数学、python语言还有我们今天要说的C++都有元组。

简单地说,元组就是一组东西,例如,在讲代数拓扑的时候,经常把拓扑空间X和其中一点x作为一个偶对(X, x),这其实就是个元组,点的坐标也可以看成一个元组。C++中的元组(tuple)是这个样子的:

std::tuple tu{ 2,"12iop" };

一个tuple可以包含不同类型的成员,例如上面的tu包含一个int和一个字符串。

二、用法

在考察源码之前,我们必须先知道它的用法。

要想使用tuple,要包含头文件:

#include

tuple实际上是一个有可变参数的类模板,使用的时候,传入若干个参数将其特化。

struct Point

{

int x;

int y;

};

void main()

{

std::tuple t1{ 1,"qwer" }; // 一个由int和字符串组成的tuple

constexpr std::tuple t2{ 1,nullptr }; // 一个由int和void*组成的tuple

std::tuple t3{ 1,{20,89} }; // 一个由int和Point结构体组成的tuple

std::tuple t4{ 1,'t',"qwer" }; // 一个由int、char、字符串组成的tuple

}

上面的代码中,我用constexpr修饰了t2,这是完全正确的,std::tuple的构造函数是constexpr的。

获取tuple中的值,用std::get。这不是函数,而是函数模板,我们需要传入size_t类型的变量将其特化,或者传入一个类型,告诉它我们需要取出元组中的哪个类型的成员。

struct Point

{

int x;

int y;

};

void main()

{

std::tuple t1{ 1,"qwer" };

constexpr std::tuple t2{ 10,nullptr };

std::tuple t3{ 1,{20,89} };

std::tuple t4{ 1,'t',"qwer" };

std::cout << std::get<0>(t1) << std::endl; // 1

constexpr int n2 = std::get<0>(t2);

std::cout << n2 << std::endl; // 10

auto s = std::get(t4);

std::cout << s << std::endl; // t

}

std::get也是constexpr的,所以n2也是一个编译时的常量。

我们通过get的方式得到了s,它是char类型的变量。std::get可以从tuple中获取到第一个类型为T的成员。

tuple也可以用【==】和【!=】比较是否相等:

std::tuple t5{ 1,"qwer" };

if (t1 == t5)

{

std::cout << "==" << std::endl;

}

介绍tuple的用法不是本文的主要内容,故到此为止。有兴趣的同学可以自行查阅资料。

接下来,是时候考察一看源码了。

三、源码分析

tuple是个可变参数的类模板:

template

class tuple;

这是对类模板的声明。

接下来,实现参数个数为零的空tuple。

3.1 tuple<>

struct allocator_arg_t

{};

template<>

class tuple<>

{

public:

typedef tuple<> _Myt;

constexpr tuple() noexcept

{}

template

tuple(allocator_arg_t, const _Alloc&) noexcept

{}

constexpr tuple(const tuple&) noexcept

{}

template

tuple(allocator_arg_t, const _Alloc&, const _Myt&) noexcept

{}

void swap(_Myt&) noexcept

{}

constexpr bool _Equals(const _Myt&) const noexcept

{

return true;

}

constexpr bool _Less(const _Myt&) const noexcept

{

return false;

}

};

allocator_arg_t是个空的结构体,暂时不管它。_Myt就是tuple<>自己,这样写起来方便一些。

tuple<>定义了空的构造函数和拷贝构造函数(空tuple没什么可做的)。

成员函数swap用于与另一个tuple<>交换内容,因为没什么可交换的,函数体当然是空的。

_Equals用来判断两个tuple<>是否相等,它返回true,这是显然的(所有的tuple<>都是一个样子)。

_Less从函数名看,是为了比较大小,但如果遇到没有重载

有了空tuple的定义,就可以定义非空的tuple。

3.2 非空的tuple

template

class... _Rest>

class tuple<_this _rest...>

: private tuple<_rest...>

{

// 内容

}

n(>0)个元素的tuple私有继承了n-1个元素的tuple。显然这是一种递归定义,最终会递归到tuple<>,而tuple<>是已经定义好了得。

例如,tuple私有继承了tuple,而tuple又私有继承了tuple,tuple私有继承了tuple<>。由于私有继承可以实现“has-a”功能,所以,这样的方式可以将不同类型的对象组合在一起。如下图:

那么,tuple是如何存储其中的元素呢?

template

class... _Rest>

class tuple<_this _rest...>

: private tuple<_rest...>

{ // recursive tuple definition

public:

typedef _This _This_type;

typedef tuple<_this _rest...> _Myt;

typedef tuple<_rest...> _Mybase;

static const size_t _Mysize = 1 + sizeof...(_Rest);

_Tuple_val<_this> _Myfirst; // 存储的元素

}

原来,它有个成员叫_Myfirst,它就是用来存储_This类型的变量的。你会看到_Myfirst的类型不是_This而是_Tuple_val<_this>,其实,_Tuple_val又是一个类模板,它的代码这里就不展开了,简而言之,它的作用是存储一个tuple中的变量。_Myfirst._Val才是真正的元素。

这个tuple只存储一个元素,类型为_Rest...的其他元素存在基类_MyBase即tuple<_rest...>中。我们仍然以tuple为例,tuple存储了一个int,有基类tuple;而tuple存储了一个char,有基类tuple;tuple存储了一个short,有基类tuple<>;tuple没有基类也不存储任何元素。

3.3 构造函数

tuple的构造函数没什么可说的,就是初始化_Myfirst和_MyBase,当然,_MyBase也要进行么一个过程,直到tuple<>。

constexpr tuple(): _Mybase(), _Myfirst()

{}

constexpr explicit tuple(const _This& _This_arg,

const _Rest&... _Rest_arg)

: _Mybase(_Rest_arg...),

_Myfirst(_This_arg)

{}

tuple(const _Myt&) = default;

tuple(_Myt&&) = default;

它还提供了默认拷贝构造函数和移动构造函数(移动语义是C++11中新增的特性,请自行查阅资料)。其实,它还有很多构造函数,写起来挺热闹,无非就是用不同的方式为它赋初值,故省略。

3.4 部分成员函数

tuple重载了赋值符号(=),这样,tuple之间是可以赋值的

template

_Myt& operator=(const tuple<_other...>& _Right)

{ // assign by copying same size tuple

_Myfirst._Val = _Right._Myfirst._Val;

_Get_rest() = _Right._Get_rest();

return (*this);

}

赋值符号返回左边的引用,这种行为和C++的内置类型是一致的。_Get_rest是tuple的成员函数,作用是把除了_Myfirst之外的那些元素拿出来。

接下来是成员函数_Equals,

template

constexpr bool _Equals(const tuple<_other...>& _Right) const

{

static_assert(_Mysize == sizeof...(_Other), "comparing tuple to object with different size");

return (_Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()));

}

其中进行了静态断言,如果两个tuple的元素个数不相同,会引发一个编译时的错误。如果对应的类型不能用==进行比较,在模板特化时也会引发编译期的错误,例如,tuple<:string int>不能和tuple比较,因为std::string和int是不能用==进行比较的。

前面提到的_Get_rest在这里:

_Mybase& _Get_rest() noexcept

{

return (*this);

}

constexpr const _Mybase& _Get_rest() const noexcept

{

return (*this);

}

它返回对基类的引用。*this的类型虽然是_Myt,但根据C++语法(可以把派生类的引用赋给对基类的引用),所以这样做是没问题的。

以上就是C++标准库元组(tuple)源码浅析的全部内容,希望对大家的学习有所帮助。相关阅读:

HTML5事件方法全部汇总

java 获取路径的各种方法(总结)

jquery实现简单的自动播放幻灯片效果

分布式队列服务MemcacheQ在Linux系统下的编译安装

基于.NET平台常用的框架和开源程序整理

(图)谷歌Chrome浏览器向Win10 Edge浏览器送庆生蛋糕

Win10已兼容绝大多数Win7/Win8.1软硬件

5种解决Java独占写文件的方法

jQuery基于ID调用指定iframe页面内的方法

AJAX提交与FORM提交的区别说明

Linux系统的密码忘记了 无法登录怎么办

linux构建动态WEB服务器安装篇

Win7/Win8.1系统检测不到KB3035583补丁无法升级Win10的解决方法

javascript获取所有同类checkbox选项(实例代码)

php 元组,C++_浅析C++标准库元组(tuple)源码,一、什么是元组 元组不是什 - phpStudy...相关推荐

  1. IAR新建stm32工程,完美移植stm32标准库(附源码)

    开发环境:Window 10 64bit 开发工具:IAR Embedded Workbench 硬件:stm32f103c8t6 基于IAR 开发工具,新建立STM32F1系列的最小开发例程,并加入 ...

  2. C++_泛型编程与标准库(十)——set与map

    C++_泛型编程与标准库(十)--set与map 参考:<侯捷泛化编程与标准库>.GNU9.3.0,vs2019 图中标红部分为自己的笔记理解 SET GNU 9.3.0的set部分代码如 ...

  3. C++_泛型编程与标准库(九)——红黑树的使用

    C++_泛型编程与标准库(九)--红黑树的使用 参考:<侯捷泛化编程与标准库>.GNU9.3.0,vs2019 图中标红部分为自己的笔记理解 struct _Rb_tree_node_ba ...

  4. C++_泛型编程与标准库(八)

    C++_泛型编程与标准库(八) 参考:<侯捷泛化编程与标准库>.GNU9.3.0,vs2019 图中标红部分为自己的笔记理解 1.array GNU 2.9的写法 array GNU9.3 ...

  5. C++_泛型编程与标准库(七)

    C++_泛型编程与标准库(七) 参考:<侯捷泛化编程与标准库>.VC2019 图中标红部分为自己的笔记理解 1.深度探索vector 笔者觉得在最新的VC2019下不是二倍增长,虽然是几何 ...

  6. C++_泛型编程与标准库(六)

    C++_泛型编程与标准库(六) 图中标红部分为自己的笔记理解 1.iterator几个特定属性 vc2019的链表list迭代器部分代码 // CLASS TEMPLATE _List_iterato ...

  7. C++_泛型编程与标准库(五)

    C++_泛型编程与标准库(五) 图中标红部分为自己的笔记理解 1.容器与容器之间的关系 2.深度探索list list是双向链表,从vc2019的list源码和以下图片都可以看出 template & ...

  8. C++_泛型编程与标准库(四)

    C++_泛型编程与标准库(四) 图中标红部分为自己的笔记理解 1.注意一个特化的typedefine 2.分配器 2.2.VC2019的allocator类 1. allocate调用operator ...

  9. C++_泛型编程与标准库(三)

    C++_泛型编程与标准库(三) 图中标红部分为自己的笔记理解 1.分配器简单测试,后面还会单讲 实测VC2019在Microsoft Visual Studio\2019\Community\VC\T ...

最新文章

  1. 升职加薪必看!如何试出一个Java开发者真正的水平
  2. c++实现sscom串口助手循环发送_串口通讯你真的会了吗?不妨看看这些经验
  3. zcmu2014(公式推导+二分)
  4. compat mysql51 5.1_MySQL mysql-5.1升级到mysql-5.6
  5. 世界上最欢乐的职业,可能就是蹦极的工作人员了!
  6. java 按位_Java中的按位运算
  7. mysql挂载数据卷_记一次生产数据库数据文件进行分区转移
  8. Yii框架多表查询(一对一与多对一)
  9. DEDECMS首页调用单页文档内容并带过滤HTML的方法
  10. 067 Pytohn文件的两种用途
  11. 七月算法机器学习4 凸优化初步
  12. 计算机考研视频哪个机构的好,计算机考研辅导视频十大排名
  13. eval('{kkk:{}}')出错,eval('{}')与eval('var ss = {kkk:{}}')正常
  14. Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'的解决办法
  15. scroll案例:带有动画的返回顶部
  16. 乒乓球十一分制比赛规则_乒乓球赛制 乒乓球十一分制比赛规则
  17. 【附源码】Java计算机毕业设计旅游管理系统(程序+LW+部署)
  18. jq 使用jSignature.js实现电子签名
  19. [574]tf.nn.xw_plus_b
  20. 笔记----SQL语句

热门文章

  1. when tired
  2. idea的必要设置jdk,maven,git
  3. 2020年中国保险科技行业发展现状及未来发展潜力分析[图]
  4. 笔记本电脑uniapp真机调试搜索不到设备
  5. 晋中中学计算机学院怎么样,山西晋中称霸一方的5所高中,其中3所是省重点中学,你知道几所?...
  6. 2021全球高被引学者榜单出炉:中科院蝉联第二,南京工业大学超过武大北航
  7. 计算机的配置的作用,电脑的配置
  8. 酒店用品批发拓客的10个经典方法
  9. PTA 补充题库 7-19 评委打分
  10. 数据链路层(内容超多哦)