一、如何创建std::tuple
主要有如下4种方式:
- std::tuple<>()
- std::forward_as_tuple()
- std::make_tuple()
- std::tie()
#include <iostream>
#include <tuple>
#include <string>
using namespace std;
static int cnt = 0;
class Person {
private:
int id;
public:
string name;
int age;
Person() {
id = ++cnt;
cout << "Person()" << id <<endl;
}
Person(string _name, int _age): name(_name), age(_age) {
id = ++cnt;
cout << "Person(string _name, int _age)" << id << endl;
}
Person(const Person&) {
id = ++cnt;
cout << "Person(const Person&)" << id << endl;
}
Person(Person&&) {
id = ++cnt;
cout << "Person(Person&&)" << id << endl;
}
Person& operator=(const Person&) {
cout << "operator=(const Person&)" << id << endl;
return *this;
}
};
int main () {
{
cout << "--------------------------------//(1)" << endl;
tuple<int, bool, string, Person> t1 = tuple<int, bool, string, Person>(11, true, "ok", Person("ok", 11));
}
{
cout << "--------------------------------//(2)" << endl;
Person p("ok", 11);
tuple<int, bool, string, Person> t2 = tuple<int, bool, string, Person>(11, true, "ok", p);
}
{
cout << "--------------------------------//(3)" << endl;
tuple<int, bool, string, Person> t3 = std::forward_as_tuple(11, true, "ok", Person("ok", 11));
}
{
cout << "--------------------------------//(4)" << endl;
Person p("ok", 11);
tuple<int, bool, string, Person> t4 = std::forward_as_tuple(11, true, "ok", p);
}
{
cout << "--------------------------------//(5)" << endl;
tuple<int, bool, string, Person> t5 = std::make_tuple(11, true, "ok", Person("ok", 11));
}
{
cout << "--------------------------------//(6)" << endl;
Person p("ok", 11);
tuple<int, bool, string, Person> t6 = std::make_tuple(11, true, "ok", p);
}
{
cout << "--------------------------------//(7)" << endl;
int i = 11;
bool b = true;
string s = "ok";
Person p("ok", 11);
tuple<int, bool, string, Person> t7 = std::tie(i, b, s, p);
}
return 1;
}
执行结果如下:
--------------------------------//(1)
Person(string _name, int _age)1
Person(Person&&)2
--------------------------------//(2)
Person(string _name, int _age)3
Person(const Person&)4
--------------------------------//(3)
Person(string _name, int _age)5
Person(Person&&)6
--------------------------------//(4)
Person(string _name, int _age)7
Person(const Person&)8
--------------------------------//(5)
Person(string _name, int _age)9
Person(Person&&)10
Person(Person&&)11
--------------------------------//(6)
Person(string _name, int _age)12
Person(const Person&)13
Person(Person&&)14
--------------------------------//(7)
Person(string _name, int _age)15
Person(const Person&)16
从执行结果可以看到,(1)(3)的效率最高,(2)(4)(7)次之,(5)更低,(6)最差。也就是说,尽量使用std::tuple<>()和std::forward_as_tuple()通过内部元素使用右值引用构造的形式;尽量不要使用std::make_tuple()的任何形式。
二、std::tuple中的元素是在堆中创建的还是在栈中创建的
std::tuple中的元素创建的区域是跟std::tuple创建的区域相同。如果std::tuple是在栈上创建的,则其中的元素也是在栈中创建的;如果std::tuple是在堆中创建的,则其中的元素也在堆中创建。
三、如何获取std::tuple中存放的元素个数
template<typename Tuple>
int getSize() {
return std::tuple_size<Tuple>::value;
};
四、如何获取std::tuple中存放的元素类型
template<typename Tuple, int N>
struct TypeGetter
{
using type = typename std::tuple_element<N, Tuple>::type;
};
N表示tuple中的第N个元素
五、如何遍历std::tuple中的元素
#include <iostream>
#include <tuple>
#include <string>
using namespace std;
template<typename Tuple, int N = std::tuple_size<Tuple>::value>
struct Printer
{
static void log(Tuple& t) {
Printer<Tuple, N - 1>::log(t);
using type = typename std::tuple_element<N - 1, Tuple>::type;
std::string ts = typeid(type).name();
type& v = std::get<N - 1>(t);
std::cout << ts << ":" << v << std::endl;
}
};
template<typename Tuple>
struct Printer<Tuple, 1>
{
static void log(Tuple& t) {
using type = typename std::tuple_element<0, Tuple>::type;
std::string ts = typeid(type).name();
type& v = std::get<0>(t);
std::cout << ts << ":" << v << std::endl;
}
};
int main() {
std::tuple<int, bool, string> t = std::forward_as_tuple(11, true, "ok");
Printer<std::tuple<int, bool, string>>::log(t);
return 1;
}
执行结果:
int:11
bool:1
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >:ok
上述代码通过定义结构体模板Printer,并对Printer做偏特化实现对tuple对象的遍历的。
六、如何通过已有的std::tuple生成新的std::tuple
主要有以下4种形式:
- 拷贝
- 转移
- 交换
- 函数返回
tuple<int, bool, string, Person> swapTest() {
{
cout << "--------------------------------//(1)" << endl;
tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
cout << "--------------------------------" << endl;
tuple<int, bool, string, Person> t1 = t0; // equal to tuple<int, bool, string, Person> t1(t0);
}
{
cout << "--------------------------------//(2)" << endl;
tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
cout << "--------------------------------" << endl;
tuple<int, bool, string, Person> t1 = move(t0); // equal to tuple<int, bool, string, Person> t1(move(t0));
}
{
cout << "--------------------------------//(3)" << endl;
tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
cout << "--------------------------------" << endl;
tuple<int, bool, string, Person> t1;
cout << "--------------------------------" << endl;
t1.swap(t0); // equal to std::swap(t1, t0);
}
cout << "--------------------------------//(4)" << endl;
tuple<int, bool, string, Person> t0(11, true, "ok", Person("ok", 11));
cout << "--------------------------------" << endl;
return t0;
}
int main() {
tuple<int, bool, string, Person> t = swapTest();
cout << "--------------------------------" << endl;
return 1;
}
运行结果:
----------------------------------------------------------------//(1)
Person(string _name, int _age)1
Person(Person&&)2
--------------------------------
Person(const Person&)3
----------------------------------------------------------------//(2)
Person(string _name, int _age)4
Person(Person&&)5
--------------------------------
Person(Person&&)6
----------------------------------------------------------------//(3)
Person(string _name, int _age)7
Person(Person&&)8
--------------------------------
Person()9
--------------------------------
Person(Person&&)10
operator=(const Person&)9
operator=(const Person&)8
----------------------------------------------------------------//(4)
Person(string _name, int _age)11
Person(Person&&)12
--------------------------------
Person(Person&&)13
--------------------------------
从运行结果中可以看到,(2)(4)效率最高,元素都是采用转移构造函数创建的,(1)次之,(3)最差。当然(3)性能最差是有原因,它毕竟是交换,需要双向赋值。
七、如何将std::tuple转化为对象
可以使用T make_from_tuple(Tuple&& t)(C++17),并且对象要的一个类型和顺序与std::tuple中元素的类型和顺序相同的构造函数。
八、如何将std::tuple中的元素拆解
1.拆解成可变参数函数
//void print()
//{
// cout << endl;
//}
//
//template <typename HeadType, typename... Types >
//void print(HeadType arg, Types... Args)
//{
// cout << typeid(HeadType).name() << ":" << arg << endl;
//
// print(Args...);
//}
template < typename Tuple, typename... Types >
constexpr void tuple_element_type(Tuple&& t, Types... Args)
{
// print(Args...);
}
template<typename Tuple, std::size_t... I>
constexpr void tuple_element_index(Tuple&& t, std::index_sequence<I...>)
{
tuple_element_type(t, std::get<I>(std::forward<Tuple>(t))...);
}
template <typename Tuple>
constexpr void break_tuple(Tuple&& t)
{
tuple_element_index(t, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
最终,将std::tuple中的元素拆解到方法void tuple_element_type(Tuple&& t, Types... Args)中。
2.拆解成变量
int main() {
std::tuple<int, std::string, bool> t(121, "dds", true);
int i;
std::string s;
bool b;
std::tie(i, s, b) = t;
cout << "i=" << i << endl;
cout << "s=" << s << endl;
cout << boolalpha << "b=" << b << endl;
return 0;
}