一、pair历史概述
- C++标准库的第1版(C++98),提供了一个简单的class,用来处理类型不同的两个(一对)值,这个就是pair。到了C++11,pair被重新定义,有了很大扩展
- pair与tuple:
二、pair概述
特点:
- 一个pair保存两个数据,pair是一个用来生成特定类型的模板
- 当创建一个pair时,我们必须提供两个类型名,pair的数据成员具有对应的类型
- C++标准库内部多出用到了pair:
- 例如容器map、multimap、unordered_map、unordered_multimap就是使用pair来管理其元素
- 例如任何函数如果需要返回两个value,也需要用到pair,例如minmax()函数
内部定义
- pair在底层被定义为一个struct,其所有成员默认都是public的

pair的操作:
- pair定义于<utility>头文件内,命名空间为std::pair
- pair提供了下面的操作:
- 原则上可以对pair<>执行create、copy/assign/swap及compare等操作
- 此外还提供了first_type、second_type类型定义式,用来表示第一value和第二value的类型

三、构造函数、赋值、移动语义
- 规则:
- ①定义pair时,不给出值时,使用默认构造函数初始化
- ②使用圆括号/花括号初始化器进行初始化
默认构造函数
- 规则:默认构造函数生成一个pair时,其元素分别执行默认构造函数
- 演示案例:
//保存两个string,两个string初始化为空
pair<string, string> anon;
//保存一个string和一个size_t,string为空,size_t为0
pair<string, size_t> word_count;
//保存一个string和一个vector容器,string和vector均为空
pair<string, vector<int>> line;
构造函数
- pair提供了三个构造函数,用来初始化first和second成员:
- 第1个和第2个:比较常见,分别传递实参给fisrt和second进行初始化
- 第3个:比较特殊,其传递两个tuple

pair<string, string> author{"James","Joyce"};
//等同于
pair<string, string> author("James","Joyce");
- 第三种构造函数比较特殊,如果想要使用,必须传递一个std::pircewise_construct作为参数1才能够使用。例如:
class Foo {
public:
Foo(tuple<int, float>) {
cout << "F::Foo(tuple)" << endl;
}
template <typename... Args>
Foo(Args... args) {
cout << "F::Foo(args...)" << endl;
}
};
int main()
{
tuple<int, float> t(1, 2.22);
pair<int, Foo> p1(42, t);
pair<int, Foo> p2(piecewise_construct, make_tuple(42), t);
return 0;
}

- 这种特殊的初始化发生在当我们需要安放(emplace())一个新元素到(unordered)map或multimap中时
拷贝构造函数
- 拷贝构造函数有3个版本:
- 版本1:接收相同类型的pair
- 版本2:是个member template,在“构造过程中需要隐式类型转换”时被调用。如果
- 版本3:隐式合成的,template形式的构造函数不会掩盖合成的拷贝构造函数。如果pair对象被复制,调用的是这个版本
- 演示案例:
void f(std::pair<int, const char*>);
void g(std::pair<const int, std::string>);
void foo()
{
std::pair<int, const char*> p(42,"hello");
f(p); //调用版本1
g(p); //调用版本2
}
- 从C++11起,如果pair中某个元素其只有一个nonconstant copy构造函数,那么将不再编译成功。例如:
class A
{
public:
A(A&);
};
int main()
{
std::pair<A, int> p; //错误
return 0;
}

赋值运算符、移动语义
五、元素访问
- 因为pair被定义为struct,因此其所有成员都是public的,可以直接访问
first、second成员
- first、second分别用来访问pair容器的第1、第2个数据成员
pair<string, string> author{ "James","Joyce" };
cout << author.first <<"\n"<< author.second << endl;
- 例如自己实现一个泛型函数模板,可以将一个pair写入stream中
template<typename T1,typename T2>
std::ostream& operator<<(std::ostream& strm, std::pair<T1, T2>& p)
{
return strm << "[" << p.first << "," << p.second << "]";
}
tuple-like接口
- 从C++11起,可以对pair使用一份tuple-like接口。这些接口本来视为tuple设计的,但是pair也可以使用(在tuple文章有详细介绍)
- 例如:
- 使用tuple_size<>::value获得元素个数
- 使用tuple_element<>::type获得某指定元素的类型
- 也可以使用get()获得first或second
typedef std::pair<int, float> IntFloatPair;
IntFloatPair p(42, 3.14);
std::get<0>(p); //等价于p.first
std::get<1>(p); //等价于p.second
std::tuple_size<IntFloatPair>::value; //返回2
std::tuple_element<0, IntFloatPair>::type num; //等价于int num
六、便捷函数make_pair()
make_pair()函数的定义

- C++11之后,pair需要应付move semantic(移动语义),因此定义修改如下:

使用演示案例
void f(std::pair<int, const char*>);
void g(std::pair<const int, std::string>);
void foo()
{
f(std::make_pair(42, "empty")); //直接创建pair
g(std::make_pair(42, "chair")); //创建pair时需要类型转换
return 0;
}
- 使用pair创建对象,其元素型是绝对的;使用make_pair创建对象,其元素类型使用默认的。例如:
- 下面pair创建的其second元素类型固定为float。make_pair创建的其second元素类型默认为double
- 这对于使用重载函数或template时,确切的类型传递十分重要
std::pair<int, float>(42, 7.77);
std::make_pair(42, 7.77);
移动语义(move semantic)
- 如果你现在make_pair()中使用move semantic语义,那么你需要显式给出std::move()声明。例如:
std::string s, t;
//使用s和t初始化一个pair,s和t使用移动语义,表示之后不再使用
auto p = std::make_pair(std::move(s), std::move(t));
- 如果你选择的是reference semantic,那么需要使用ref(),强制形成一个reference类型,或使用cref()强制形成一个constant reference类型(二者由<functional>提供)。例如:
int i = 0;
auto p = std::make_pair(std::ref(i), std::ref(i));
++p.first;
++p.second;
std::cout << "i:" << i << std::endl; //打印2
七、std::tie()接口
- C++11起,可以使用定义于<tuple>内的tie()接口,抽取出pair的value
- 例如,下面的p(pair类型)被赋值给一个tuple类型,后者第二value是个reference,指向c
std::pair<char, char> p = std::make_pair('x', 'y');
char c;
std::tie(std::ignore, c) = p;
std::cout << c << std::endl; //y
八、pair作为函数返回值的使用
- 如果pair作为函数返回值返回,则可以使用下面的方式:
- 隐式/显式构造返回
- 初始化器返回
- 使用make_pair<>创建pair对象返回
演示案例:
pair<string, int> process(vector<string> &v)
{
if (!v.empty())
return{ v.back(),v.back().size() }; //初始化器返回
else
return pair<string, int>();//隐式构造返回
//return pair<string, int>(v.back(),v.back().size);//显示构造返回
}
- 在C++早期版本,不允许使用初始化器,因此只能使用显式构造
- 也可以使用make_pair<>创建pair对象返回
if (!v.empty)
return make_pair{ v.back(),v.back().size };
九、pair之间的比较
- 相等运算符:只有当两个pair对象内的所有元素都相等,才视这两个pair对象相等

- 比较运算符:此处以<为例。第一元素具有较高的优先级,如果比较成功就不再继续比较;如果相等,继续接着比较

十、 pair运用实例
- C++标准库大量使用pair:
- 例如(unordered)map和multimap容器的元素类型都是pair,也就是一对key/vaule
- C++标准库中凡“必须返回两个value”的函数都是用pair对象