C++(标准库):02---pair容器

本文深入解析C++标准库中的pair类,介绍其历史背景、基本特性、构造与赋值、移动语义、元素访问方式、便捷函数make_pair的使用、与tuple的交互、作为函数返回值的技巧以及在C++标准库中的广泛应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、pair历史概述

二、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之前,这个函数的声明和定义如下L

  • 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对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值