从了解到手撕:C++ 标准库利器 std::pair 全方位指南
·
从了解到手撕:C++ 标准库利器 std::pair 全方位指南
std::pair是什么
- 核心概念:
std::pair是一个标准库模板,它用于把两个相同或不同的类型的值组合成一个单一的对象 - 具象化理解:你可以把它想象成一个坐标点,一个坐标点
(x, y)中必然包含两个x和y两个值,他们共同构成了一个整体你可以把它当做是一个坐标点,可以将两个数据“捆绑”在一起,例如你可以使用下面这行代码来表示一个人的姓名和年龄
std::pair<std::string, int>
为什么需要std::pair ——告别繁琐的定义结构体
-
场景:当一个函数需要两个返回值怎么办?
- 传统做法:自定义一个结构体,修改函数的返回值为
struct name或者提供一个输出型参数,但是这样会影响代码的可读性,显得很笨重 std::pair的优雅之处:提供了一个轻量级、标准化的解决方案,无需额外定义结构体即可方便地组合和传递两个值。最典型的应用就是在std::map中作为键值对的存储单元。
- 传统做法:自定义一个结构体,修改函数的返回值为
标准库中对于pair的介绍
-
我们来看一下cplusplus对
pair的介绍
-
介绍中说“这个类把一对值耦合在一起,它们可能是不同的类型,各自的值可以通过公有成员
first和second访问”,pair是tuple的一个特例,tuple可以构造多个值返回 -
成员类型和成员变量()
- 它需要两个模板参数,并提供两个公有成员
first和second用于访问值这两个模板参数
- 它需要两个模板参数,并提供两个公有成员
-
它的成员函数很简单,构造函数,operator=以及swap
-
接下来我们简单演示一下如何使用
-
创建与初始化:
-
显式构造:
std::pair<int, std::string> p1(1, "hello"); -
使用
std::make_pair:auto p2 = std::make_pair(2, "world"); -
优势:利用模板参数推导,代码更简洁。
-
-
访问元素:
-
访问第一个元素:
p1.first -
访问第二个元素:
p1.second
-
-
做函数返回值:
std::pair<bool, int> find_element(const std::vector<int>& vec, int value) {
for (size_t i = 0; i < vec.size(); ++i) {
if (vec[i] == value) {
return {true, i}; // C++11 及以上版本支持列表初始化
}
}
return {false, -1};
}
- 在
std::map中使用:
也可以直接使用{}构造,会走隐式类型转换
std::map<std::string, int> word_counts;
word_counts.insert(std::make_pair("apple", 5));
word_counts["banana"] = 10;
动手实现一个自己的 pair—— 揭开神秘面纱
-
了解了pair的一些相关知识后,我们来动手实现一个自己的
pair把 -
第一步:定义模板结构体
-
我们需要一个接受两种类型的模板结构体
Pair。 -
它包含两个公开成员变量
first和second。 -
提供一个构造函数来初始化这两个成员。
-
template <typename T1, typename T2>
struct pair
{
public:
T1 first;
T2 second;
};
- 按照前面我们介绍的函数进行实现,这里我还提供了移动构造,移动语义是C++11提出的新概念,如果提供了移动构造函数,在进行大量值的拷贝时,转移资源的所有权,而不是深拷贝
pair() : first(), second() {}
pair(const T1 &t1, const T2 &t2) : first(t1), second(t2) {}
template <typename F, typename S>
pair(const pair<F, S> &other) : first(other.first), second(other.second) {}
// 移动语义的诉求是转移资源,避免深拷贝,为了提高性能,这里如果给了模板参数,
// 传进来的参数类型和现在不一样,一来要走隐式类型转换,二来还可能进行深拷贝,不符合移动语义的诉求
pair(const pair &&other) : first(std::move(other.first)), second(std::move(other.second)) {}
// 移动赋值运算符
pair &operator=(const pair &&other)
{
first = std::move(other.first);
second = std::move(other.second);
return *this;
}
void swap(pair &other)
{
swap(first, other.first);
swap(second, other.second);
}
- 类内还提供了一个模板函数,可能会需要使用已经存在的
pair进行构造一个新的pair,如果原来的数据类型是float,需要一个double类型,这里可以方便使用,而不需要重新定义一个pair
template <typename F, typename S>
pair(const pair<F, S> &other) : first(other.first), second(other.second) {}
- 接下来我们在实现一个
make_pair,它用于构造一个pair对象并返回
template <typename T1, typename T2>
const pair<T1, T2> make_pair(const T1 first, const T2 second)
{
return pair<T1, T2>(first, second);
}
现代C++对std::pair的一个扩展
- 如果想要访问pair的两个值,我们需要使用
first和second,这样写有点麻烦,C++17中提供了一种更方便的访问pair的成员的方法——结构化绑定(Structured Bindings),可以通过auto直接获取值
std::pair<std::string, int> person("Alice", 25);
auto [name, age] = person; // 直接解包到变量 name 和 age
std::cout << name << " is " << age << " years old." << std::endl;
std::tuple的简单介绍
- 可以看到标准库的介绍“tuple是一个可以容纳多个元素的对象,每个元素都可以是不同类型”,从介绍我们就可以看出为什么在
pair的介绍那里说pair是tuple的一个特例了
- 简单使用
// tuple example
#include <iostream> // std::cout
#include <tuple> // std::tuple, std::get, std::tie, std::ignore
int main ()
{
std::tuple<int,char> foo (10,'x');
auto bar = std::make_tuple ("test", 3.1, 14, 'y');
std::get<2>(bar) = 100; // access element
int myint; char mychar;
std::tie (myint, mychar) = foo; // unpack elements
std::tie (std::ignore, std::ignore, myint, mychar) = bar; // unpack (with ignore)
mychar = std::get<3>(bar);
std::get<0>(foo) = std::get<2>(bar);
std::get<1>(foo) = mychar;
std::cout << "foo contains: ";
std::cout << std::get<0>(foo) << ' ';
std::cout << std::get<1>(foo) << '\n';
return 0;
}
- 如何选择
pair和tuple?
-如果你需要返回两个值,那么就优先使用pair,多个值就使用tuple,还是按照个人喜好来
总结与展望
- 这篇文章讲了关于C++的
std::pair的使用与实现,std::pair是一个用于捆绑两个值的轻量级、标准化的容器,是 C++ 程序员工具箱中不可或缺的一员,知道怎么用,更要知道背后的原理。 - 了解了
pair后,相信你对tuple也应该能有了一个简单的认识,如果感兴趣你可以阅读cplusplus-std::tuple
对std::pair的介绍就到这里了,如果觉得写的还不错的话,欢迎点赞加关注,如果有写的不对的地方,欢迎批评指正( •̀ ω •́ )✧
更多推荐



所有评论(0)