C++之tuple

  tuple是TR1引入的东西,它扩展了pair的概念,拥有任意数量的元素。也就是说,tuple呈现出一个异质元素列,其中每个元素类型都可以被指定,或来自编译其推导。C++11中,variadic template被引入,使template可以接受任意数量的实参。在<tuple>中的class tuple的声明为

namespace std { 
	template <typename... Types>
	class tuple;
}

tuple的常用操作如下表

操作含义
tuple<T1,T2,...,Tn> t以n个给定类型的元素建立一个tuple,以各元素类型的默认构造函数完成初始化(基础类型的初值为0)
tuple<T1,T2,...,Tn> t(v1,v2,...,vn)以n个给定类型的元素建立一个tuple,以给定值完成初始化
tuple<T1, T2> t(p)建立一个tuple,带有两个元素,使用给定的pair p的元素类型和值进行初始化
t=t2将t2赋值给t
t=ppair p赋值给一个带有两个元素的tuple
t1==t2比较两个tuple是否相等(如果所有元素的==比较结果都是true,就返回true
t1!=t2判断t1与t2是否不相等(!(t1==t2)
t1<t2t1是否小于t2,使用字典式比较
t1>t2t1是否大于t2
t1<=t2t1 小于等于t2
t1>=t2t1大于等于t2
t1.swap(t2)互换t1和t2的数据(始自C++11)
swap(t1,t2)互换t1和t2的数据(全局函数,始自C++11)
make_tuple(v1,v2,...)以传入的所有数值和类型建立一个tuple,并允许由此tuple提取数值
tie(ref1,ref2,...)建立一个由reference构成的tuple,并允许由此tuple提取个别数值

1. tuple的操作

  tuple的接口比较直观:

  • 通过明白的声明,或通过便捷函数make_tuple(),可以创建一个tuple
  • 通过get<>()函数模板,可以访问tuple的元素

下面的语句创建一个异质的四元素tuple

tuple<string, int, int, complex<double>> t;

每个元素的内容由默认构造函数初始化,基础类型都被初始化为0(这项保证始自C++11)。下面的语句建立并初始化一个异质的三元素tuple

tuple<int, float, string> t1(41, 6.3, "nico");

  也可以使用make_tuple()建立tuple,其所有元素类型都从初值自动推导,如

make_tuple(22, 44, "nico");

建立和初始化一个元素类型分别为intintconst char*tupletuple的元素类型可以是引用,如:

string s;
tuple<string&> t(s);
get<0>(t)="hello";   // s的值变为"hello"

tuple不是寻常的容器,不允许迭代元素。对于tuple可以使用其成员函数来处理元素,因此必须在编译期知道需要处理的元素的索引值。如可以这样处理tuple t1的第一元素:

get<0>(t1)

运行期才传入一个索引值是不被允许的:

int i;
get<i>(t1)		// 编译错误,i不是编译时的值

  此外,tuple还提供惯常的拷贝、赋值和比较操作。它们身上都允许发生隐式类型转换,但元素个数比较绝对吻合。如果两个tuple的所有元素都相等,它们就整体相等。检查某个tuple是否小于另一个tuple,采用的是lexicographical(字典编纂式的)比较法则。

2. 便捷函数make_tuple()tie()

  便捷函数make_tuple()会根据值建立tuple,不需要明确指出元素类型。借由特别的函数对象reference_wrapper<>及便捷函数ref()cref()(都是自C++11起被定义于<functional>),可以影响make_tuple()产生的类型,如以下表达式产生的tuple带有一个引用,指向对象s

string s;
make_tuple(ref(s));		// tuple<string&>

运用引用并搭配make_tuple(),就可以提取tuple的元素值,如

std::tuple <int, float, std::string>	t(77, 1.1, "more light");
int i;
float f;
std::string s;
// 将t的值分配给i, f和s
std::make_tuple(std::ref(i), std::ref(f), std::ref(s)) = t;

  如果想方便地在tuple中使用引用,可以选择tie(),它可以建立一个内含引用的tuple

std::tuple <int, float, std::string> t(77,1.1,"more light");
int i;
float f;
std::string s;
std::tie(i,f,s) = t; 	// 将t的值分配给i, f和s

这里的std::tie(i,f,s)会以i, f和s的引用建立起一个tuple,因此上述赋值操作其实就是将t内的元素分别赋值给i、f和s。
 &ems;使用tie()时,std::ignore允许我们忽略““tuple中的某些元素,也就是说可以提取局部的tuple“`的元素值:

std::tuple <int, float, std::string> t(77,1.1,"more light");
int i;
std::string s;
std::tie(i,std::ignore,s) = t;	// 将t的第一和第三个元素分配给i和s

3. tuple和初值列(Initializer List)

  各个构造函数中,“接受不定个数的实参”的版本被声明为explicit

namespace std{ 
	template <typename... Types>
	class tuple { 
	public:
		explicit tuple(const Types&...);
		template <typename... UTypes> explicit tuple(UTypes&&...);
		...
	};
}

这是为了避免单一值被隐式转换为“带一个元素的tuple”:

template <typename... Args>
void foo(const std::tuple<Args...> t);

foo(42);	// ERROR
foo(make_tuple(42));	// OK

这种情况下,不可以使用赋值语法将某个tuple初始化,因为那会被视为一个隐式转换:

std::tuple<int, double>	t1(42, 3.14);	// OK, 老的语法
std::tuple<int, double>	t1{ 42, 3.14};	// OK, 新的语法
std::tuple<int, double>	t1={ 42, 3.14};	// ERROR

对于tuple,必须明确地将初值转化为一个tuple(如运用make_tuple()):

std::vector<std::tuple<int, float>> v{ std::make_tuple(1,1.0),
									 std::make_tuple(2,2.0) }; //OK
std::tuple<int, int, int> foo() { 
	return std::make_tuple(1,2,3);	// OK
}

std::tuple<int, int, int> foo() { 
	return { 1,2,3};	// ERROR
}

4. 其它的tuple特性

  有些辅助函数是特别为tuple而设计的,特别是为了支持泛型编程:

  • tuple_size<tupletype>::value 可获得元素个数
  • tuple_element<idx, tupletype>::type 可取得第idx个元素的类型(也就是get()返回值的类型)
  • tuple_cat() 可将多个tuple串接成一个tuple

5. tuple的输入输出

  tuple最初公开于Boost程序库,在那,tuple可以将其元素写至output stream, 但C++标准库并不支持这个性质。以下头文件可以使用标准输出操作符<<打印任何tuple

// util/printtuple.hpp
#include <tuple>
#include <iostream>

// print element with index IDX of tuple with MAX elements
template <int IDX, int MAX, typename... Args>
struct PRINT_TUPLE { 
    static void print (std::ostream& strm, const std::tuple<Args...>& t){ 
        strm << std::get<IDX>(t) << (IDX+1 == MAX ? "" : ",");
        PRINT_TUPLE<IDX+!, MAX, Args...>::print(strm, t);
    }
};

// partial specialization to end the recursion
template <int MAX, typename... Args>
struct PRINT_TUPLE<MAX,MAX,Args...> { 
    static void print(std::ostream& strm, const std::tuple<Args...>& t){ 
    }
};

// output operator for tuples
template <typename... Args>
std::ostream& operator << (std:: ostream& strm, const std::tuple<Args...>& t){ 
    strm << "[";
    PRINT_TUPLE<0,sizeof...(Args),Args...>::print(strm,t);
    return strm << "]";
}

这段代码大量运用模板超编程,在编译期就递归迭代tuple的所有元素,每次调用PRINT_TUPLE<>::print()就打印出一个元素,然后调用相同函数打印下一个元素。一个偏特化版本(其“当前索引IDX”和“tuple内的元素个数MAX”相等)来终结递归调用。

6. tuplepair转换

  可以用一个pair作为初值,初始化一个双元素tuple,也可以将一个pair赋值给一个双元素tuple。;注意,pair<>提供了一个特殊构造函数,以tuple为初值。

    原文作者:不入流的IT宅男
    原文地址: https://blog.csdn.net/Function_RY/article/details/104125468
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞