本系列博文中我们使用同类容器(如数组类型)来阐述模板的强大威力,同时,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. MAC配置Xcode的Cocos2d-x环境

    Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00003988 1.Mac配置环境变量,即编辑命令: o ...

  2. 如何进行Java EE性能测试与调优

    性能测试的目标 性能测试不同于功能测试,不是对与错的检验,而是快与慢的衡量.在进行真正的性能测试之前要先搞清楚目标: 1. 在确定的硬件条件下,可以支持的并发数越大越好,响应时间越快越好.具体需要达到 ...

  3. (笔记)Linux下如何查看高CPU占用率线程

    在 Linux 下 top 工具可以显示 cpu 的平均利用率(user,nice,system,idle,iowait,irq,softirq,etc.),可以显示每个 cpu 的利用率.但是无法显 ...

  4. 第三百四十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy模拟登陆和知乎倒立文字验证码识别

    第三百四十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy模拟登陆和知乎倒立文字验证码识别 第一步.首先下载,大神者也的倒立文字验证码识别程序 下载地址:https://gith ...

  5. JAVA平台在手机上广泛应用

    JAVA平台由于在手机上广泛应用,使得扩展名为jar的游戏成为目前手机游戏市场上最大的家族,直接传入手机直接安装即可. 众所周知,JAVA是一种跨平台的程序设计语言.由于其高可移植性.简单.可靠.安全 ...

  6. Linux free命令使用及解析

    1. 命令格式 free [参数] 2. 命令功能 free 命令显示系统使用和空闲的内存情况,包括物理内存.交互区内存(swap)和内核缓冲区内存.共享内存将被忽略 3. 命令参数 -b 以Byte ...

  7. 刚看完了一本关于javascript的书感觉受益匪浅,原来不懂的东西这么多,想问问怎么成为大神?求教!!!!!!

    刚看完了一本关于javascript的书感觉受益匪浅,原来不懂的东西这么多,想问问怎么成为大神?求教!!!!!!

  8. mysql的字符串函数

    From: http://www.cnblogs.com/xiaochaohuashengmi/archive/2010/12/13/1904330.html 对于针对字符串位置的操作,第一个位置被标 ...

  9. C语言中的数组问题

    数组默认最后一位是 结束符 占一位, 假如是7个字节大小的数组 实际输入为6个字节,最后一个字节为'\0' 这样写 char password_set[7]={"123456"}; ...

  10. POI初体验

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 它的结构如下: HSSF - 提供读写Micros ...