本系列博文中我们使用同类容器(如数组类型)来阐述模板的强大威力,同时,C/C++还具有包含异类对象的能力。这里的异类指的是类型不同,或者结构不同。tuple就是这样的一个类模板,它能够用于聚集不同类型的对象,本篇博文旨在介绍可以聚集任意个数的成员对象。
------------------------------------------------------------------------------------------------------------

21.1 duo
自定义的duo的目的是把两个对象聚集到一个单一类型(类似标准库的std::pair)。

template <typename T1, typename T2>
struct Duo
{//add2: 提供域类型的访问typedef T1 Type1;             // 第1个域的类型typedef T2 Type2;             // 第2个域的类型enum { N = 2 };                    // 域的个数// end add2
T1 v1;       // 第1个域的值T2 v2;       // 第2个域的值//add1: 并且给它添加两个构造函数
    Duo() : v1(), v2() { }Duo(T1 const&a, T2 const& b) : v1(a), v2(b) { }// end add1
};// 辅助函数
template <typename T1, typename T2>
inline
Duo<T1, T2> make_duo(T1 const& a, T2 const& b)
{return Duo<T1, T2>(a, b);
}// 对于某些需要判断返回结果是否有效的函数而言会很有用
Duo<bool, X> result = foo();
if (result.v1)
{// 结果是有效的,返回值是result.v2
    ...
}// 创建和初始化Duo也非常简单
make_duo(true, 42);

上面实现的duo已经很接近std::pair了,但还有一些不同之处,如在构造函数中我们没有提供用于隐式类型转换的成员模板初始化函数;没有提供比较运算符等;基于这些区别,我们下面给出一个更强大清晰的实现:

// tuples/duo1.hpp

#ifndef DUO_HPP
#define DUO_HPPtemplate <typename T1, typename T2>
class Duo
{public:// 提供域类型的访问typedef T1 Type1;             // 第1个域的类型typedef T2 Type2;             // 第2个域的类型enum { N = 2 };                    // 域的个数private:T1 value1;       // 第1个域的值T2 value2;       // 第2个域的值public:// 并且给它添加两个构造函数
        Duo() : value1(), value2() { }Duo(T1 const&a, T2 const& b) : value1(a), value2(b) { }// 用于在构造期间,进行隐式的类型转换template <typename U1, typename U2>Duo(Duo<U1, U2> const& d): value1(d.v1()), value2(d.v2()) { }// 用于在赋值期间,进行隐式的类型转换template<typename U1, typename U2>Duo<T1, T2>& operator= (Duo<U1, U2> const& d){value1 = d.value1;value2 = d.value2;return *this;}// 用于访问域的函数(域访问函数)T1& v1(){return value1;}T1 const& v1() const {return value1;}T2& v2(){return value2;}T2 const& v2() const {return value2;}
};// 比较运算符(允许混合类型):
template<typename T1, typename T2, typename U1, typename U2>
inline
bool operator==(Duo<T1, T2> const& d1, Duo<U1, U2> const& d2)
{return d1.v1() == d2.v1() && d1.v2() == d2.v2();
}template<typename T1, typename T2, typename U1, typename U2>
inline
bool operator!=(Duo<T1, T2> const& d1, Duo<U1, U2> const& d2)
{return !(d1 == d2);
}// 针对创建和初始化的辅助函数
template<typename T1, typename T2>
inline
Duo<T1, T2> make_duo(T1 const& a, T2 const& b)
{return Duo<T1, T2>(a, b);
}#endif // DUO_HPP// tuples/duo1.cpp
#include "duo1.hpp"
Duo<float, int> foo()
{// Duo<int, int> 到返回类型Duo<float, int>的隐式转型return make_duo(42, 42);
}int main()
{// Duo<float, int> 到返回类型Duo<int, double>的隐式转型if (foo() == make_duo(42, 42.0)){...}
}

21.2 可递归duo
21.2.1 域的个数
书中在本节提供了一个简单的可递归duo:

// tuples/duo2.hpp
template <typename A, typename B, typename C>
class Duo<A, Duo<B, C> >
{public:typedef A            T1;       // 第1个域的类型typedef Duo<B, C>            T2;       // 第2个域的类型enum { N = Duo<B, C>::N + 1 };            // 域的个数private:T1 value1;                // 第1个域的值T2 value2;                // 第2个域的值public:// 其他的公共成员都不需要改变
        .....
};// tuples/duo6.hpp
// 相应的递归出口
template <typename A>
struct Duo<A, void>
{public:typedef A        T1;          // 第1个域的类型typedef void        T2;          // 第2个域的类型enum { N = 1 };            // 域的个数private:T1 value1;                // 第1个域的值public:// 构造函数
        Duo() : value1() {}Duo(T1 const& a) : value1(a) {}// 域访问函数T1& v1(){return value1;}T1 const& v1() const{return value1;}void v2() {}void v2() const{}....
};

21.2.2 域的类型
用于获取duo的第N个域的类型(即T)的基本模板

// (1)对于non-Duo(非duo)而言,结果类型为void
template <int N, typename T>
class DuoT
{public:typedef void ResultT;         // 一般情况下,结构类型是void
};// (2)对于非递归的duo,定义两个简单的局部特化,用于获取每个域的类型
// 针对普通duo第1个域的特化
template <typename A, typename B>
class DuoT<1, Duo<A, B> >
{public:typedef A ResultT;
};// 针对普通duo第2个域的特化
template <typename A, typename B>
class DuoT<2, Duo<A, B> >
{public:typedef B ResultT;
};// (3)可递归duo的第N个域的类型:一般情况下,它等于第2个域的第N-1个域的类型
// 针对可递归duo第N个域的类型的特化
template <int N, typename A, typename B, typename C>
class DuoT<N, Duo<A, Duo<B, C> > >
{public:typedef typename DuoT<N-1, Duo<B, C> >::ResultT ResultT;
};// (4)另外,针对可递归duo第1个(域的)类型的特化如下
// 针对可递归duo第1个域的特化
template <typename A, typename B, typename C>
class DuoT<1, Duo<A, Duo<B, C> > >
{public:typedef A ResultT;
};// 针对可递归duo第2个(域的)类型的特化如下
// 针对可递归duo第2个域的特化
template <typename A, typename B, typename C>
class DuoT<2, Duo<A, Duo<B, C> > >
{public:typedef B ResultT;
};

21.2.3 域的值
在一个可递归duo中,就操作而言,抽取第N个值与抽取第N个类型是类似的,只是抽取第N个值要稍微复杂一些。为了能够抽取第N个值,我们需要实现一个形为val<N>(duo)的接口。但是在实现该接口的过程中,我们需要先实现一个辅助类模板DuoValue,因为只有类模板才能够被局部特化(函数模板现在还不可以),而局部特化能够帮助我们高效地抽取第N个值。如下:

// tuples/duo5.hpp

#include "typeop.hpp"// 返回变量duo的第N个值
template <int N, typename A, typename B>
inline
typename TypeOp<typename DuoT<N, Duo<A, B> >::ResultT>::RefT
val(Duo<A, B>& d)
{return DuoValue<N, Duo<A, B> >::get(d);
}// 返回常量duo的第N个值
template <int N, typename A, typename B>
inline
typename TypeOp<typename DuoT<N, Duo<A, B> >::ResultT>::RefConstT
val(Duo<A, B> const& d)
{return DuoValue<N, Duo<A, B> >::get(d);
}

下面是DuoValue的一个完整实现

// tuples/duo4.hpp

#include "typeop.hpp"
//基本模板,针对(duo)T的第N个值
template <int N, typename N>
class DuoValue
{public:static void get(T&) { }        // 一般情况下,并不返回值static void get(T const&) { }
};// 针对普通duo的第N个域的特化
template <int N, typename N>
class DuoValue<1, Duo<A, B> >
{public:static A& get(Duo<A, B> & d) {return d.v1();}static A const& get(Duo<A, B> const&) {return d.v1();}
};// 针对普通duo第2个域的特化
template <typename A, typename B>
class DuoValue<2, Duo<A, B> >
{public:static B& get(Duo<A, B> &d){return d.v2();}static B const& get(Duo<A, B> const &d){return d.v2();}
};// 针对可递归duo的第N个值的特化
template <int N, typename A, typename B, typename C>
struct DuoValue<N, Duo<A, Duo<B, C> > >
{statictypename TypeOp<typename DuoT<N-1, Duo<B, C> >::ResultT>::RefTget(Duo<A, Duo<B, C> > &d){return DuoValue<N-1, Duo<B, C> >::get(d.v2());}static typename TypeOp<typename DuoT<N-1, Duo<B, C>>::ResultT>::RefConstTget(Duo<A, Duo<B, C> > const &d){return DuoValue<N-1, Duo<B, C> >::get(d.v2());}
};// 针对可递归duo的第1个域的特化
template <typename A, typename B, typename C>
class DuoValue<1, Duo<A, Duo<B, C> >
{public:static A& get(Duo<A, Duo<B, C> > &d){return d.v1();}static A const& get(Duo<A, Duo<B, C> > const &d){return d.v1();}
};// 针对可递归duo的第2个域的特化
template <typename A, typename B, typename C>
class DuoValue<2, Duo<A, Duo<B, C> > >
{public:static B& get(Duo<A, Duo<B, C> > &d){return d.v2().v1();}static B const& get(Duo<A, Duo<B, C> > const &d){return d.v2().v1();}
};

下面程序给出如何使用上面的duo:

// tuples/duo5.cpp

#include "duo1.hpp"
#include "duo2.hpp"
#include "duo3.hpp"
#include "duo4.hpp"
#include "duo5.hpp"#include <iostream>int main()
{// 创建和使用一个简单的duoDuo<bool, int> d;std::cout << d.v1() << std::endl;std::cout << val<1>(d) << std::endl;// 创建和使用tripleDuo<bool, Duo<int, float> > t;val<1>(t) = true;val<2>(t) = 42;val<3>(t) = 0.2;std::cout << val<1>(t) << std::endl;std::cout << val<2>(t) << std::endl;std::cout << val<3>(t) << std::endl;
}

例如,调用:

val<3>(t)

最后将会扩展为:

t.v2().v2()

21.3 tuple构造
上一节我们了解到可递归duo的嵌套结构有助于展现metaprogramming技术的应用,现在我们为把该结构封装成一个简单接口,从而可以在日常工作中使用这种结构。为了实现这种接口,我们可以定义一个含有多个参数的可递归tuple模板,并让它派生自一个可递归duo类型,其中该duo类型的域个数是有限制的(假设最多5个域)。
为了使tuple的大小(即域个数)是可变的,我们声明了一些无用的类型参数,它们缺省值是一个null类型;在此,我们特地定义了一个NullT类型,用于代表这种null类型。之所以使用NullT,而不使用void,是因为我们需要创建该类型(即NullT)的参数,而void是不能作为参数类型的:

// 用于代表无用类型参数的类型
class NullT
{
};

接下来,我们把tuple定义为一个模板,它派生自duo,而且该duo至少具有一个定义为NullT的类型参数:

// 一般情况下,Tuple<>都创建自“至少含有一个NullT的另一个Tuple<>”
template <typename P1,typename P2 = NullT,typename P3 = NullT,typename P4 = NullT,typename P5 = NullT>
class Tuple : public Duo<P1, typename Tuple<P2, P3, P4, P5, NullT>::BaseT>
{public:typedef Duo<P1, typename Tuple<P2, P3, P4, P5, NullT>::BaseT> BaseT;// 构造函数
        Tuple() { }Tuple(TypeOp<P1>::RefConstT a1,Tuple(TypeOp<P2>::RefConstT a2,Tuple(TypeOp<P3>::RefConstT a3 = NullT(),Tuple(TypeOp<P4>::RefConstT a4 = NullT(),Tuple(TypeOp<P5>::RefConstT a5 = NullT()): BaseT(a1, Tuple<P2, P3, P4, P5, NullT>(a2, a3, a4, a5)){   // 递归减少参数个数
        }
};// 用于终止递归的特化
template <typename P1, typename P2>
class Tuple<P1, P2, NullT, NullT, NullT> : public Duo<P1, P2>
{public:typedef Duo<P1, P2> BaseT;Tuple() { }Tuple(TypeOp<P1>::RefConstT a1,Tuple(TypeOp<P2>::RefConstT a2,Tuple(TypeOp<NullT>::RefConstT = NullT(),Tuple(TypeOp<NullT>::RefConstT = NullT(),Tuple(TypeOp<NullT>::RefConstT = NullT()): BaseT(a1, a2){}
};

于是,有一个如下的声明:

Tuple<bool, int, float, double> t4(true, 42, 13, 1.95583);

最后的层次体系如下图21.1所示:

而其他的特化将会考虑tuple是一个singleton(即只具有一个域)的情形:

// 针对singletons的特化
template <typename P1>
class Tuple<P1, NullT, NullT, NullT, NullT> : public Duo<P1, void>
{public:typedef Duo<P1, void> BaseT;Tuple() { }Tuple(TypeOp<P1>::RefConstT a1,Tuple(TypeOp<NullT>::RefConstT = NullT(),Tuple(TypeOp<NullT>::RefConstT = NullT(),Tuple(TypeOp<NullT>::RefConstT = NullT(),Tuple(TypeOp<NullT>::RefConstT = NullT()): BaseT(a1){}
};

最后,我们定义类似make_duo()的辅助函数,对每种不同大小的tuple,都需要声明一个不同的函数模板make_duo(),因为函数模板不能含有缺省模板实参,而且在模板参数的演绎过程中,也不会考虑缺省的函数调用实参:

// 针对一个实参的辅助函数
template <typename T1>
inline
Tuple<T1> make_duo(T1 const& a1)
{return Tuple<T1>(a1);
}// 针对两个实参的辅助函数
template <typename T1, typename T2>
inline
Tuple<T1, T2> make_duo(T1 const& a1, T2 const& a2)
{return Tuple<T1, T2>(a1, a2);
}// 针对3个实参的辅助函数
template <typename T1, typename T2, typename T3>
inline
Tuple<T1, T2, T3> make_duo(T1 const& a1, T2 const& a2, T3 const& a3)
{return Tuple<T1, T2, T3>(a1, a2, a3);
}// 针对4个实参的辅助函数
template <typename T1, typename T2, typename T3, typename T4>
inline
Tuple<T1, T2, T3, T4> make_duo(T1 const& a1, T2 const& a2, T3 const& a3, T4 const& a4)
{return Tuple<T1, T2, T3, T4>(a1, a2, a3, a4);
}// 针对5个实参的辅助函数
template <typename T1, typename T2, typename T3, typename T4, typename T5>
inline
Tuple<T1, T2, T3, T4, T5> make_duo(T1 const& a1, T2 const& a2, T3 const& a3, T4 const& a4, T5 const& a5)
{return Tuple<T1, T2, T3, T4, T5>(a1, a2, a3, a4, a5);
}

下面的程序给出如何使用该tuple:

// tuples/tuple1.cpp

#include "tuple1.hpp"
#include <iostream>int main()
{// 创建和使用只具有1个域的tupleTuple<int> t1;val<1>(t1) += 42;std::cout << t1.v1() << std::endl;// 创建和使用duoTuple<bool, int> 42;std::cout << val<1>(t2) << ", " ;std::cout << t2.v1() << std::endl;// 创建和使用tripleTuple<bool, int, double> t3;val<1>(t3) = true;val<2>(t3) = 42;val<3>(t3) = 0.2;std::cout << val<1>(t3) << ", ";std::cout << val<2>(t3) << ", ";std::cout << val<3>(t3) << std::endl;t3 = make_tuple(false, 23, 13.13);std::cout << val<1>(t3) << ", ";std::cout << val<2>(t3) << ", ";std::cout << val<3>(t3) << std::endl;// 创建和使用quadrupleTuple<bool, int, float, double> t4(true, 42, 13, 1.95583);std::cout << val<4>(t4) << std::endl;std::cout << t4.v2().v2().v2() << std::endl;
}

转载于:https://www.cnblogs.com/yyxt/p/5217240.html

C++ template —— tuple(十三)相关推荐

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

    浅析C++标准库元组(tuple)源码 一.什么是元组 元组不是什么新鲜东西,在数学.python语言还有我们今天要说的C++都有元组. 简单地说,元组就是一组东西,例如,在讲代数拓扑的时候,经常把拓 ...

  2. python内置数据结构_Python内置数据结构(一)

    1.列表 (1)列表初始化 (2)下标/索引操作 (3)修改列表元素 (4)增加列表元素 1)append 2)insert 3)extend (5)删除列表元素 1)pop 2)remove 3)c ...

  3. C++11 FAQ中文版

    C++11 FAQ中文版 http://www.chenlq.net/cpp11-faq-chs http://www.stroustrup.com/C++11FAQ.html Morgan Stan ...

  4. vue超详细教程,手把手教你完成vue项目

    Vue 一. Vue简介 ​ Vue是于2013年(与React框架同年发布)推出的一个渐进式.自底向上的前端框架,它的作者叫尤雨溪.那么什么叫做渐进式框架呢?比较官方的说法就是:以Vue内核作为核心 ...

  5. Visual Studio2012 编译 gtest 遇到 error C2977: ‘std::tuple‘ : too many template argum...

    问题描述: 今天想试用visual stuido11做个小程序,打算做成TDD的样子,编译gtest的时候报如下的错: test\gtest.h(9735): error C2977: 'std::t ...

  6. 【弄nèng - Elasticsearch】DSL入门篇(十三)—— Index Template

    文章目录 1. 简介 2. 创建模板 3.查询模板 4.删除模板 项目推荐 索引模板 官网传送门 参考: https://www.cnblogs.com/shoufeng/p/10641560.htm ...

  7. c++标准库第五章通用工具5.1Pari和Tuple

    一.pair 提供了直接访问对应数据成员的能力,因为pair是struct而不是class,默认所有成员都是public 二.所有元素都相等,这两个pair对象才相等 三.如果first相等,才继续比 ...

  8. C++11中std::tuple的使用

    std::tuple是类似pair的模板.每个pair的成员类型都不相同,但每个pair都恰好有两个成员.不同std::tuple类型的成员类型也不相同,但一个std::tuple可以有任意数量的成员 ...

  9. 第二十三模板 18标准模板库

    //第二十三模板 18标准模板库 //1 容器 容器是包含其他对像的对像,标准C++ 库中提供了一系列的容器类 //可以分为两种类型,顺序和关联类型,顺序容器可提供对自身元素的顺序或者随机访问,关联容 ...

最新文章

  1. 标准h5的定位_H5中的定位
  2. escape()、encodeURI()、encodeURIComponent()区别详解
  3. 【荐】说说CSS Hack 和向后兼容
  4. js自动判断浏览器类型跳转到手机版
  5. LeetCode 电子书!
  6. 再有人问你volatile是什么,就把这篇文章发给他
  7. android 参数 attrs.xml,使用attrs.xml自定义属性
  8. 蓝桥杯入门训练序列求和
  9. Hexo + Github搭建自己的博客
  10. 处理效应模型stata实例_手把手教你Stata软件操作与案例分析
  11. [Python程序设计] 用Scrapy爬取学堂在线计算机类课程页面的信息
  12. linux java调优
  13. Kubernetes
  14. 解决Maven仓库代理时gexin的jar包一直下载不下来的问题
  15. (附源码)springboot大学生防疫封校管理系统 毕业设计632124
  16. Armstrong 一个n位数等于各个位数n次方之和
  17. 算法笔记随笔:分数的化简,四则运算和输出
  18. BME相关SCI期刊
  19. dtree做权限控制
  20. synergy服务器未响应,请问synergy启动不了怎么回事???

热门文章

  1. centos7杀掉进程_Centos常用命令,查看进程、杀死进程、启动进程等常用命令
  2. python如何启动excel_如何用python打开excel
  3. 【统计和图形分析】上海道宁为您带来测试、分析、改进和控制自身服务、交易和制造流程的强大工具——SigmaXL
  4. 一年加班几百小时,我是二胎宝妈,更是敢拼的前端女程序员
  5. 数学实验测试软件,matlab数学软件实验测试题.doc
  6. 无线连接IPTV完整教程
  7. PR 2019 快速入门(17)
  8. vm眼影真的好用吗_vm拜金主义眼影盘值得买吗?vm拜金主义眼影如何配色
  9. 《软件定义数据中心:Windows Server SDDC技术与实践》一导读
  10. MATLAB在线测试