本系列博文中我们使用同类容器(如数组类型)来阐述模板的强大威力,同时,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 = }; // 域的个数
// 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, );

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

// tuples/duo1.hpp

#ifndef DUO_HPP
#define DUO_HPP template <typename T1, typename T2>
class Duo
{
public:
// 提供域类型的访问
typedef T1 Type1; // 第1个域的类型
typedef T2 Type2; // 第2个域的类型
enum { N = }; // 域的个数 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(, );
} int main()
{
// Duo<float, int> 到返回类型Duo<int, double>的隐式转型
if (foo() == make_duo(, 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 + }; // 域的个数 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 = }; // 域的个数 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<, Duo<A, B> >
{
public:
typedef A ResultT;
}; // 针对普通duo第2个域的特化
template <typename A, typename B>
class DuoT<, 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-, Duo<B, C> >::ResultT ResultT;
}; // (4)另外,针对可递归duo第1个(域的)类型的特化如下
// 针对可递归duo第1个域的特化
template <typename A, typename B, typename C>
class DuoT<, Duo<A, Duo<B, C> > >
{
public:
typedef A ResultT;
}; // 针对可递归duo第2个(域的)类型的特化如下
// 针对可递归duo第2个域的特化
template <typename A, typename B, typename C>
class DuoT<, 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<, 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<, 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> > >
{
static
typename TypeOp<typename DuoT<N-, Duo<B, C> >::ResultT>::RefT
get(Duo<A, Duo<B, C> > &d){
return DuoValue<N-, Duo<B, C> >::get(d.v2());
} static typename TypeOp<typename DuoT<N-, Duo<B, C>
>::ResultT>::RefConstT
get(Duo<A, Duo<B, C> > const &d){
return DuoValue<N-, Duo<B, C> >::get(d.v2());
}
}; // 针对可递归duo的第1个域的特化
template <typename A, typename B, typename C>
class DuoValue<, 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<, 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()
{
// 创建和使用一个简单的duo
Duo<bool, int> d;
std::cout << d.v1() << std::endl;
std::cout << val<>(d) << std::endl; // 创建和使用triple
Duo<bool, Duo<int, float> > t; val<>(t) = true;
val<>(t) = ;
val<>(t) = 0.2; std::cout << val<>(t) << std::endl;
std::cout << val<>(t) << std::endl;
std::cout << val<>(t) << std::endl;
}

例如,调用:

val<>(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, , , 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个域的tuple
Tuple<int> t1;
val<>(t1) += ;
std::cout << t1.v1() << std::endl; // 创建和使用duo
Tuple<bool, int> ;
std::cout << val<>(t2) << ", " ;
std::cout << t2.v1() << std::endl; // 创建和使用triple
Tuple<bool, int, double> t3; val<>(t3) = true;
val<>(t3) = ;
val<>(t3) = 0.2; std::cout << val<>(t3) << ", ";
std::cout << val<>(t3) << ", ";
std::cout << val<>(t3) << std::endl; t3 = make_tuple(false, , 13.13);
std::cout << val<>(t3) << ", ";
std::cout << val<>(t3) << ", ";
std::cout << val<>(t3) << std::endl; // 创建和使用quadruple
Tuple<bool, int, float, double> t4(true, , , 1.95583);
std::cout << val<>(t4) << std::endl;
std::cout << t4.v2().v2().v2() << std::endl;
}

C++ template —— tuple(十三)的更多相关文章

  1. C++中map的概念,与简单操作

     来源:http://blog.csdn.net/wallwind/article/details/6876892 C++map学习   map<Key, Data, Compare, Allo ...

  2. python之字符串

    字符串与文本操作 字符串: Python 2和Python 3最大的差别就在于字符串 Python 2中字符串是byte的有序序列 Python 3中字符串是unicode的有序序列 字符串是不可变的 ...

  3. mac安装protobuf2.4.1时报错./include/gtest/internal/gtest-port.h:428:10: fatal error: 'tr1/tuple' file not found和google/protobuf/message.cc:175:16: error: implicit instantiation of undefined template

    通过网上下载的protobuf2.4.1的压缩文件,然后进行安装,./configure和make时遇到了两个问题. 正常的安装步骤如下: ./configure make  make check m ...

  4. python3 第十三章 - 数据类型之tuple(元组)

    元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可: language = ('c', 'c++', 'py ...

  5. C#设计模式之十三模板方法模式(Template Method Pattern)【行为型】

    一.引言 “结构型”的设计模式已经写完了,从今天我们开始讲“行为型”设计模式.现在我们开始讲[行为型]设计模式的第一个模式,该模式是[模板方法],英文名称是:Template Method Patte ...

  6. C++标准库 -- tuple

    头文件:<tuple> 可访问属性: 无(用get方法来访问数据) 可访问方法: swap(tuple) 和另外一个tuple交换值 其他相关方法: swap(t1, t2) 交换两个tu ...

  7. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

  8. tuple解包给类的构造函数

    首先我们的第一步当然是将tuple解包.tuple提供了一个get函数来获取第N个元素.例如: get<1>(make_tuple(...)); 要将一个tuple全部拆解,就可以使用通过 ...

  9. C++14使用std::integer_sequence展开tuple作为函数的参数

    元组是一种长度固定的允许有不同类型元素的集合,根据元素的个数不同又分别称作一元组.二元组.三元组等.C++11中标准库增加了一个叫std::tuple的类模板,用于表示元组. 下面的代码演示了使用C+ ...

随机推荐

  1. 关于Unity中RectTransform和Transform

    以前一直以为在Inspector面板上的是Transform,后来才发现原来2D是RectTransform,3D是Transform 3D面板上显示的是位置坐标组件Transform,2D面板上显示 ...

  2. 【CUDA学习】内核程序调试

    调试工具 cuda-gdb,网上有英文版的说明文档. 其中大部分调试命令和gdb的调试命令相同. cuda程序分为主机端程序和设备端程序,主机端程序调试也就是C语言程序的调试 主要是设备端程序,关键点 ...

  3. 初试PyOpenGL二 (Python+OpenGL)基本地形生成与高度检测

    在上文中,讲述了PyOpenGL的基本配置,以及网格,球形的生成,以及基本的漫游.现在利用上一篇的内容,来利用高程图实现一个基本的地形,并且,利用上文中的第三人称漫游,以小球为视角,来在地形上前后左右 ...

  4. SAP 以工序为基准进行发料 机加工行业 Goods Issue to Routing

    SAP 以工序为基准进行发料   这个流程是在业务有关需求,业务需要按照工序发料,一个工单有多个工序,而料是要发到每个工序上,而且没到工序之间在物理上是有距离的,所以仓管员在打印配发单之后希望了解到哪 ...

  5. python一天一题(2)

    python查询mysql数据库 import pymysql host = '192.168.74.5' user = 'root' passwd ='root' port = 3310 db = ...

  6. MySQL迁移数据库(mysqldump)

    一.导出导入所有数据库的数据 1.导出 mysqldump -u root -p123456 --all-databases > all.sql 2.导入 mysql -u root -p123 ...

  7. WCF Service 配置文件注释(转)

    VS 2008 SP1(不确定是否不打SP1是否有)自带的一个编辑工具,可以更快的帮助定制配置文件, 以前看到过没有注意, 昨天正好一个同事提起, 这里记录一笔:打开VS 2008->Tools ...

  8. python3 post方式上传文件。

    借助第三方库:Requests 其官网地址:   http://python-requests.org       官网上写的安装方式:http://docs.python-requests.org/ ...

  9. thinkphp中的类库与引用import引入机制

    ThinkPHP的类库包括基类库和应用类库 控制器类 模块名+Action 例如 UserAction.InfoAction 模型类 模型名+Model 例如 UserModel.InfoModel ...

  10. UFLDL教程练习(exercise)答案(2)

    主成分分析与白化,这部分很简单,当然,其实是用Matlab比较简单,要是自己写SVD分解算法,足够研究好几个月的了.下面是我自己实现的练习答案,不保证完全正确,不过结果和网站上面给出的基本一致. 1. ...