C++之std::tie 元组创建与解包工具
C++新特性整理目录:
- C++11新特性(总结)
- C++14新特性(总结)
- C++17新特性(总结)
- C++20新特性(总结)
- C++23新特性(总结)
- C++新特性对比说明(11、14、17、20、23)
- C++新特性详细对比表(11、14、17、20、23)
std::tie是C++11提供的一个非常实用的用于解包元组或结构体、创建元组的左值引用的工具, 其定义在 头文件中,它创建一个元组,其中每个元素都是传入参数的左值引用。
其原型如下所示template< class... Types > constexpr std::tuple<Types&...> tie(Types&... args) noexcept;从原型上,我们可以看出,其输入是一系列左值引用,返回值是一个tuple(元组)
std::tie主要有两大功能:解包元组或结构体、创建元组的左值,下面我将分别介绍
创建元组的左值
我们先来看下面这样一段代码,及其运行结果吧
int main() { int a1 = 100; double b1 = 3.14; std::string c1="tie create tuple"; auto tup1 = std::tie(a1, b1, c1); tup1 = std::make_tuple(50, 12.33,"tie create tuple new"); std::cout << "a1=" << a1 << " b1=" << b1 << " c1=" << c1 << std::endl; }其输出结果如下所示:
我们通过修改元组tup1 ,成果的修改了a1,b1,c1的值了,这说明通过tie创建的元组,是我们对应的每一个变量的左值引用。是不是很好玩?但这在实际中有什么作用呢?嗯~,我能想到的应该就是:一次性交换多个变量吧,减少代码量,哈哈,起码这是一个优势,难道不是吗?(欢迎各位给出不同的看法啦)
好了,接下来我们来看看他的第二个应用吧:
解包元组或结构体
我们也从一小段代码开始吧,如下所示
int main() { std::tuple<int,double,std::string> tup2(100, 3.14, "tie create tuple"); int a1; double b1; std::string c1; tie(a1, b1, c1) = tup2; std::cout << "a1=" << a1 << " b1=" << b1 << " c1=" << c1 << std::endl; }其运行结果如下:
我们成功将元组tup2里面的值,解包到变量a1、b1、c1里面了,结合前面“创建元组的左值”的功能,这其实也不难理解了,可以翻译成这样:auto tup1 = std::tie(a1, b1, c1); std::tuple<int,double,std::string> tup2(100, 3.14, "tie create tuple"); tup1 = tup2;是不是好理解多了?这里我相信大家应该都会思考这样一个问题,我们修改a1、b1、c1的值,tup2的内容是否会跟着改变呢?我们不妨来试试看吧:
int main() { int a2; double b2; std::string c2; std::tuple<int, double, std::string> tup2 = std::make_tuple(50, 12.33, "tie create tuple new"); tie(a2, b2, c2) = tup2; std::cout << "a2=" << a2 << " b2=" << b2 << " c2=" << c2<< std::endl; a2 = 100; b2 = 3.14; c2 = "tie create tuple"; std::cout << "get<0>:"<<std::get<0>(tup2) << " get<1>:" << std::get<1>(tup2) << " get<2>:" << >std::get<2>(tup2) << std::endl; }运行结果如下:
tup2的内容没有跟着改变,这是为什么呢?显然:std::tie(a, b, c) = some_tuple 执行的是值拷贝,修改 a, b, c 不会影响原始元组。
那要如何实现这种解包修改呢?在C++17之前,我们可以这样做auto a2 = std::ref(std::get<0>(tup2)); auto b2 = std::ref(std::get<1>(tup2)); auto c2 = std::ref(std::get<2>(tup2)); a2.get() = 100; b2.get() = 3.14; c2 .get() = "tie create tuple";
std::tie解包元组或结构体基本用法
下面我将分别介绍std::tie支持解包的数据类型
(1)std::tupleint a2; double b2; std::string c2; std::tuple<int, double, std::string> tup2 = std::make_tuple(50, 12.33, "tie create tuple new"); tie(a2, b2, c2) = tup2; std::cout << "a2=" << a2 << " b2=" << b2 << " c2=" << c2<< std::endl;(2)std::pair
int a2; std::string b2; std::pair<int,std::string> pair1 = std::make_pair(50, "tie create pair"); tie(a2, b2) = pair1; std::cout << "a2=" << a2 << " b2=" << b2 << std::endl;(3)自定义类,提供as_tuple()
在网上查资料时,说可以通过特化std::tuple_size 、std::tuple_element以及get 函数实现,但是经过实测后,好像编译不通过(本人测试了c++14、17、20均无法编译通过),测试代码如下:#include <iostream> #include <tuple> #include <vector> class Employee { private: std::string name_; int id_; std::string department_; public: Employee(std::string name, int id, std::string dept) : name_(std::move(name)), id_(id), department_(std::move(dept)) { } //// 提供 get 方法供特化使用 //template<size_t I> //auto get() const { // if constexpr (I == 0) return name_; // else if constexpr (I == 1) return id_; // else if constexpr (I == 2) return department_; //} // 读的get必须实现哦,不然编译没办法通过 template<size_t Index> friend auto get(const Employee& item); // 写的get,当需要能够改变值时,需要实现哦,看你对类的封装层度来选择吧 template<size_t Index> friend auto& get(Employee& item); //// 提供 get 方法供特化使用 //template<size_t I> //auto get() const { // if constexpr (I == 0) return name_; // else if constexpr (I == 1) return id_; // else if constexpr (I == 2) return department_; //} }; // 特化 std::tuple_size namespace std { template<> struct tuple_size<Employee> : integral_constant<size_t, 3> {}; template<size_t Index> struct tuple_element<Index, Employee> { using Step1 = remove_const_t<decltype(get<Index>(declval<Employee>()))>; using Step2 = remove_volatile_t<Step1>; using type = remove_reference_t<Step2>; }; //// 特化 std::tuple_element //template<> //struct tuple_element<0, Employee> { using type = std::string; }; //template<> //struct tuple_element<1, Employee> { using type = int; }; //template<> //struct tuple_element<2, Employee> { using type = std::string; }; } // 提供非成员 get 函数 template<size_t Index> auto get(const Employee& e) { if constexpr (Index == 0) return e.name_; else if constexpr (Index == 1) return e.id_; else if constexpr (Index == 2) return e.department_; } template<size_t Index> auto& get(Employee& e) { if constexpr (Index == 0) return e.name_; else if constexpr (Index == 1) return e.id_; else if constexpr (Index == 2) return e.department_; } //// 提供非成员 get 函数 //template<size_t I> //auto get(const Employee& e) { // return e.get<I>(); //} int main() { Employee emp{ "Charlie", 101, "Engineering" }; std::string name; int id; std::string dept; // 现在可以直接使用 tie 解包 std::tie(name, id, dept) = emp; // 通过std::tie解包编译不通过,但是通过结构化绑定可以,说明我们的特化代码没有问题 //{ // auto [a1, b1, c1] = emp; // std::cout << "a=" << a1 << " b=" << b1 << " c=" << c1 << std::endl; //} //auto& [a2, b2, c2] = emp; //a2 = a2 + "1111"; /*{ auto [a1, b1, c1] = emp; std::cout << "a=" << a1 << " b=" << b1 << " c=" << c1 << std::endl; }*/ /* std::cout << "Employee: " << name << " (ID: " << id << "), Dept: " << dept << std::endl;*/ }编译错误信息如下:
所有我们还是老老实实实现as_tuple()函数吧,代码如下:
class Employee { private: std::string name_; int id_; std::string department_; public: Employee(std::string name, int id, std::string dept) : name_(std::move(name)), id_(id), department_(std::move(dept)) { } //直接返回 std::tie 的结果 auto as_tuple() const { return std::tie(name_, id_, department_); //return std::make_tuple(name_, id_, department_); } };
std::tie高阶使用场景
1、函数多返回值处理
std::tuple<int, std::string, double, bool, std::vector<int>> getComplexData() { return { 42, "answer", 3.14, true, {1, 2, 3} }; } int main() { int important_number; std::string important_text; // 只关心前两个返回值,忽略其他 std::tie(important_number, important_text, std::ignore, std::ignore, std::ignore)= getComplexData(); std::cout << important_number << ": " << important_text << std::endl; }2、选择性解包(忽略某些返回值):使用 std::ignore 忽略不需要的值
std::tie(important_number, important_text, std::ignore, std::ignore, std::ignore) = getComplexData();
谈到解包,C++17给我们提供了一个新的语法糖“结构化绑定”,其具有比std::tie更全面、更强大的功能,更简便的语法。结构化绑定从某种层面可以看做是对std::tie的增强与扩展。详见:https://blog.csdn.net/m0_45074715/article/details/154694945?spm=1011.2415.3001.5331
更多推荐







所有评论(0)