了解过python的程序员都应该知道元素的概念,纵观传统C++中的容器,除了std::pair外,似乎没有现成的结构能够用来存放不同类型的数据(通常我们会自己定义结构),但std::pair只能保留两个元素。因此C++11引入了tuple扩展了pair的概念,能够拥有任意数量的元素
元组是”多元数组“的缩写
#include <tuple>
#include <iostream>
#include <variant>
auto get_student(int id)
{
if (id == 0)
return std::make_tuple(3.8, 'A', "John");
if (id == 1)
return std::make_tuple(2.9, 'C', "Jack");
if (id == 2)
return std::make_tuple(1.7, 'D', "Ive");
// return type is std::tuple<double, char, std::string>
return std::make_tuple(0.0, 'D', "null");
}
template <size_t n, typename... T>
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i) {
if constexpr (n >= sizeof...(T))
throw std::out_of_range("out of range.");
if (i == n)
return std::variant<T...>{ std::in_place_index<n>, std::get<n>(tpl) };
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
}
template <typename... T>
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i) {
return _tuple_index<0>(tpl, i);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
std::visit([&](auto && x){ s << x;}, v);
return s;
}
int main()
{
auto student = get_student(0);
std::cout << "ID: 0, "
<< "GPA: " << std::get<0>(student) << ", "
<< "Grade: " << std::get<1>(student) << ", "
<< "Name: " << std::get<2>(student) << '\n';
double gpa;
char grade;
std::string name;
// tuple unpack
std::tie(gpa, grade, name) = get_student(1);
std::cout << "ID: 1, "
<< "GPA: " << gpa << ", "
<< "Grade: " << grade << ", "
<< "Name: " << name << '\n';
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
std::cout << std::get<std::string>(t) << std::endl;
// std::cout << std::get<double>(t) << std::endl; // illegal, runtime error
std::cout << std::get<3>(t) << std::endl;
// concat
auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
// iteration
for(int i = 0; i != tuple_len(new_tuple); ++i) {
std::cout << tuple_index(new_tuple, i) << std::endl; // runtime indexing
}
}
© 2021 GitHub, Inc.
//创建一个四元素的元组
// -元素初始化为默认值(基本类型为0)
tuple<string,int,int,complex<double>> t;
//创建并初始化一个元组
tuple<int,float,string> t1(41,6.3,"nico");
//使用make_tuple()创建元组:自动根据value建立tuple,因此不需要明确指出元素的类型
auto t2 = make_tuple(22,44,"nico");
//将t2中的第二个值赋给t1
get<1>(t1) = get<1>(t2);
//比较和赋值
// -包含从tuple到tuple的类型转换
if (t1 < t2) { // compares value for value
t1 = t2; // OK, assigns value for value
}
#include <iostream>
#include <tuple>
#include <complex.h>
using namespace std;
int main()
{
char ch = 'x'; short sh = 12; int num = 1234567; double db = 123; const char *p = "abc"; string s;
tuple <char, short, int, double, const char * , string &>mytuple(ch, sh, num, db, p, s);
// "iterate"遍历元素: tuple不是寻常的元素,不允许迭代元素[数组中唯一的例外]。可以使用成员函数get来处理元素
// 因此必须在编译期就知道你打算处理的元素的索引
auto autov0 = get<0>(mytuple); auto autov1 = get<1>(mytuple);
auto autov2 = get<2>(mytuple); auto autov3 = get<3>(mytuple);
auto autov4 = get<4>(mytuple); auto autov6 = get<5>(mytuple) = "hello";
cout << autov0 << endl;cout << autov1 << endl; cout << autov2 << endl;
cout << autov3 << endl;cout << autov4 << endl; cout << autov6 << endl;
cin.get();
}
tuple与引用
#include <iostream>
#include <tuple>
#include <complex.h>
#include <functional>
using namespace std;
int main()
{
std::string s;
auto x = std::make_tuple(s);
std::get<0>(x) = "hello world";
printf("s= %s, x = %s\n", s.c_str(), std::get<0>(x).c_str()) ; //s= , x = hello world
s = "";
auto y = std::make_tuple(ref(s));
std::get<0>(y) = "hello world";
printf("s= %s, x = %s\n", s.c_str(), std::get<0>(y).c_str()) ; //s= hello world, x = hello world
cin.get();
}
提取tuple中的元素
#include <iostream>
#include <tuple>
#include <complex.h>
#include <functional>
using namespace std;
int main()
{
tuple<int,float,string> t1(41,6.3,"nico");
int i; float f; std::string s;
//method 1:
std::make_tuple(std::ref(i), std::ref(f), std::ref(s)) = t1;
printf("%d, %f, %s\n", i, f, s.c_str());
// method 2:
i = 0; s = "", f = 0.0;
std::tie(i, std::ignore, s) = t1;
printf("%d, %f, %s\n", i, f, s.c_str());
cin.get();
}
tuple输出到output stream
tuple最初公开于boost,但是boost中可以将元素写到output stream, 而C++标准库不支持这个功能。除非你有如下头文件:
//printtuple.hpp
#include <tuple>
#include <iostream>
// helper: print elements with index IDX and higher of tuple t having 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+1,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 << "]";
}
使用方法:
#include <tuple>
#include <iostream>
#include <string>
using namespace std;
int main()
{
tuple <int,float,string> t(77,1.1,"more light");
cout << "io: " << t << endl;
}
tuple与初值列
各个构造函数中,”接受不定个数的实参“被声明为explicti:
这是为了避免单一值被隐式转换为“带着一个元素”的tuple:
tuple与其他容器使用上的不同:
但是对pair<>和容器(除了array)是行得通的
对于tuple,正确的做法是明确将初值转换为一个tuple,比如运用make_tuple