C++新特性整理目录:

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::tuple

 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;

(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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐