在 C++ 中 pair 对组类型可以将两个相同或者不同类型的值视为一个单元,例如 (1, 2.2)(1 为 int 类型,2.2 为 double 类型)。

一、头文件

要想使用 C++ 的 Pair 类型,你必须包含头文件 <utility>

二、源码定义

这里代码并不显示全部,主要是为了方便理解和研究。当然,这段代码也可能和真实的源代码有一些出入,但是请放心它是绝对正确的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace std {
template <class T1, class T2>
struct pair
{
// type name for the values
typedef T1 first_type;
typedef T2 second_type;

// member
T1 first;
T2 second;

/* defualt constructor
* T1() and T2() force initialization for build-in types
*/
pair() : first(T1()), second(T2()) {}

// constructor for two values
pair(const T1& a, const T2& b) : first(a), second(b) {}

// copy constructor with implicit conversions
template <typename U, typename V>
pair(const pair<U, V>& p) : first(p.first), second(p.second) {}
};

// comparisons
template <class T1, class T2>
bool operator== (const pair<T1, T2>&, const pair<T1, T2>&);
template <class T1, class T2>
bool operator< (const pair<T1, T2>&, const pair<T1, T2>&);
// similar: !=, <=, >, >=, ...

// convenience function to create a pair
template <class T1, class T2>
pair<T1, T2> make_pair(const T1&, const T2&);
}

三、源码解析

3.1 属性

在 pair 类型中,我们定义了两个主要的属性,他们分别是 firstsecond,而这两个属性分别对应对组变量中的第一个元素值和第二个元素值

我们可以像下面的方式来访问对组中的元素值:

1
2
3
4
5
6
{
// ...
make_pair(1, 2.2).first; // 获取元素值 1
make_pair(1, 2.2).second; // 获取元素值 2.2
// make_pair() 可以快速构造并返回一个对组对象
}

3.2 默认构造函数

根据 pair 类型的定义,我们不难看出:如果我们在创建一个对组对象时没有给定具体的元素值,那么对组类型的默认构造函数会为我们自动的调用对应元素类型的初始化构造函数进行初始化操作。例如:

1
2
3
4
{
// ...
std::pair<int, float> p;
}

按照上述代码进行创建对组对象时,p 的第一元素和第二元素就会分别调用 int()float() 来初始化,而这两个函数都将返回零值。也就是说,你最后会得到一个 (0, 0.0) 的对组对象 p

3.3 双参构造函数

这个构造函数中规中矩,比较简单,这里不赘述了。

3.4 拷贝构造函数

我们可以看到,在 pair 类型的定义中使用了 template 形式的拷贝构造函数。而之所以要这么做,是为了保证 pair 类型在拷贝构造中元素类型的隐式转换。如果 pair 对象被复制(不需要元素类型的隐式转换),调用的是由系统隐式生成的那个拷贝构造函数。例如:

1
2
3
4
5
6
7
8
void f(pair<int, const char*>);
void g(pair<const int, const string>);
void foo()
{
pair<int, const char*> p(42, "hello");
f(p); // OK: 调用由系统隐式生成的拷贝构造函数
g(p); // OK: 调用模板形式的拷贝构造函数(需要元素类型的隐式转换)
}

template 形式的构造函数不会遮掩(由编译器)隐式生成的 defualt 构造函数。

3.5 重定义比较运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace std {
// 相等比较
template <class T1, class T2>
bool operator== (const pair<T1, T2>& x, const pair<T1, T2>& y)
{
return x.first == y.first && x.second == y.second;
}

// 小于比较
template <class T1, class T2>
bool operator< (const pair<T1, T2>& x, const pair<T1, T2>& y)
{
return x.first < y.first ||
(!(x.first > y.first) && x.second < y.second)
}
}
  • 相等比较 在对组类型中,只有当两个对组对象的第一元素和第二元素均相等时,我们才认为两个对组对象相等。
  • 小于比较 在对组类型中,两个对组对象相互比较时,第一元素具有较高的优先级。也就是说,如果两个对组对象的第一元素**不相等**时,那么二者第一元素的大小关系就决定了二者的大小关系。只有当第一元素**相等**时,才会继续比较第二元素,并把比较结果当作整体的比较结果。所以,在小于比较时第一元素的大小关系将反应对组对象的大小关系。只有第一元素相等时,第二元素的大小关系才能影响对组对象的大小关系(注意观察源码)。
  • 其他 其他的比较也如法炮制...

3.6 make_pair 函数

这个函数可以更加方便的创建出一个 pair 类型的对象,因为你无需指定元素的具体类型。源码定义如下:

1
2
3
4
5
6
7
8
namespace std {
// create value pair only by providing the values
template <class T1, class T2>
pair<T1, T2> make_pair(const T1& x, const T2& y)
{
return pair<T1, T2>(x, y);
}
}

你可以这样使用该函数:

1
2
3
4
5
6
{
// ...
make_pair(42, 7.77); // 等价于 pair<int, double>(42, 7.77)
// 第二元素为 double 类型,
// 是因为“无任何修饰词的浮点数字面常数”,其类型被视为 double 类型
}

值得注意的是,使用 make_pair() 并不会多花你任何执行时间,因为编译器会将此一动作进行优化。

参考文献

  1. C++ reference pair
  2. 《C++ 标准库程序》