本系列博文中我们使用同类容器(如数组类型)来阐述模板的强大威力,同时,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. linux计算文件大小

  2. Axiom3D:Ogre地形组件代码解析

    大致流程. 这里简单介绍下,Axiom中采用的Ogre的地形组件的一些概念与如何生成地形. 先说下大致流程,然后大家再往下看.(只说如何生成地形与LOD,除高度纹理图外别的纹理暂时不管.) 1.生成T ...

  3. python 进行后端分页详细代码

    后端分页 两个接口 思路: 1. 先得到最大页和最小页数(1, 20) --> 传递给前端, 这样前端就可以知道有多少个页数 2. 通过传递页数得到当前页对应数据库的最大值和最小值 3. 通过s ...

  4. ecmall数据库表详解 二次开发必备

    文章分类表ecm_acategory 字段 类型 Null 默认 注释 cate_id int(10) 否   自增ID号,分类ID号 cate_name varchar(100) 否   分类的名称 ...

  5. (笔记)Linux下的CGI和BOA使用期间遇到的问题汇总

    前段时间在做C/S模式下的视频监控,这段时间是B/S模式下的.期间遇到了不少问题,有些问题一卡就是几天,有些问题的解决办法在办法在网上也不是很好找,所以还有些问题虽然得到了临时解决,但是其原理现在我本 ...

  6. 第三百四十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—编写spiders爬虫文件循环抓取内容—meta属性返回指定值给回调函数—Scrapy内置图片下载器

    第三百四十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—编写spiders爬虫文件循环抓取内容—meta属性返回指定值给回调函数—Scrapy内置图片下载器 编写spiders爬虫文件循环 ...

  7. Xianfeng轻量级Java中间件平台:权限管理

    权限管理:是通过系统对用户的行为进行控制的一套业务规则,可以做得很简单,比如通过硬编码的方式进行控制,也可以做得很复杂,比如通过一些复杂的权限模型去实现一些复杂的权限控制,比如菜单访问权限.按钮操作权 ...

  8. Logback中文文档(四):Appender

    什么是 Appender Appender是负责写记录事件的组件.Appender 必须实现接口"ch.qos.logback.core.Appender".该接口的重要方法总结如 ...

  9. result源码

    CREATE TABLE `result` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`thetime` CHAR(100) , `category ...

  10. CocoaPods 第三方库管理器

    下载地址:https://github.com/kattrali/cocoapods-xcode-plugin 跟 VVDocumenter 规范注释生成器的安装方式一样: 下载开源工程在 Xcode ...