前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++(STL):01---pair容器

C++(STL):01---pair容器

作者头像
用户3479834
发布2021-02-03 12:32:26
1.6K0
发布2021-02-03 12:32:26
举报
文章被收录于专栏:游戏开发司机

一、pair历史概述

  • C++标准库的第1版(C++98),提供了一个简单的class,用来处理类型不同的两个(一对)值,这个就是pair。到了C++11,pair被重新定义,有了很大扩展
  • pair与tuple:
    • tuple在TR1被引入,它是对pair的扩展
    • tuple在后面详细概述。

二、pair概述

特点:

  • 一个pair保存两个数据,pair是一个用来生成特定类型的模板
  • 当创建一个pair时,我们必须提供两个类型名,pair的数据成员具有对应的类型
  • C++标准库内部多出用到了pair:
    • 例如容器map、multimap、unordered_map、unordered_multimap就是使用pair来管理其元素
    • 例如任何函数如果需要返回两个value,也需要用到pair,例如minmax()函数

内部定义

pair在底层被定义为一个struct,其所有成员默认都是public的

代码语言:javascript
复制
namespace std { 
template <typename T1,typename T2>
struct pair {
// member 
T1 first;
T2 second;
};
}

pair的操作:

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

操作函数

影响

pair<T1,T2>P

Default构造函数,建立一个pair,其元素类型分别为T1和T2,各自以其default构造函数初始化

pair<T1,T2>p(vall,vall)

建立-一个pair,元素类型分别为T1和T2,以vall和vall为初值

pair<T1,T2>p(rvl,rv2)

建立一个pair,元素类型分别为T1和T2,以rvl和

rv2进行搬移式初始化(moveinitialized)

pair<T1,T2>p(piecewise_construct,tI,t2)

建立-一个pair,元素类型分别为tupleT1和T2,以tuple tI和t2的元素为初值

pair<T1,T2>p(p2)

Copy构造函数,建立p成为p2的拷贝

pair<T1,T2>p(n)

Move构造函数,将rv的内容移至p(允许隐式类型转换)

p=p2

将p2赋值给p(始自C++11;允许隐式类型转换)

P=rv

将rv的值moveassign给p(始自C++11;允许隐式类型转换)

p.first

获得pair内的第--value(直接成员访问)

p.second

获得pair内的第二value(直接成员访问)

get<0>(p)

等价于p.first(始自C++11)

get<1>(p)

等价于p.second(始自C++11)

p1==p2

返回“是否p1等于p2”(等价于

p1.first==p2.first始p1.second==p2.second)

p1!=p2

返回“是否p1不等于p2"(!(p1==p2))

p1<p2

返回“是否p1小于p2"(比较first,如果相等则比较second)

p1>p2

返回“是否p1比p2更大”(亦即p2<p1)

p1<=p2

返回“是否p1小于等于p2”(!(p2<p1))

p1>=p2

返回“是否p1大于等于p2”(!(p1<p2))

p1.swap(p2)

互换p1和p2的数据(始自C++11)

swap(p1,p2)

同上(是个全局函数)(始自C++11)

make_pair(vall,val2)

返回一个pair,带有vall和val2的类型和数值

三、构造函数、赋值、移动语义

  • 规则:
    • ①定义pair时,不给出值时,使用默认构造函数初始化
    • ②使用圆括号/花括号初始化器进行初始化

默认构造函数

  • 规则:默认构造函数生成一个pair时,其元素分别执行默认构造函数
  • 演示案例:
代码语言:javascript
复制
//保存两个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
  • 演示案例:下面是一般的初始化,使用普通的构造函数
代码语言:javascript
复制
pair<string, string> author{"James","Joyce"};
//等同于pair<string, string> author("James","Joyce");
  • 第三种构造函数比较特殊,如果想要使用,必须传递一个std::pircewise_construct作为参数1才能够使用。例如:
代码语言:javascript
复制
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;
}

  • 上面程序的输出如下:
代码语言:javascript
复制
F::F00(tuple)

F::Foo(args...)
  • 这种特殊的初始化发生在当我们需要安放(emplace())一个新元素到(unordered)map或multimap中时

拷贝构造函数

  • 拷贝构造函数有3个版本:
    • 版本1:接收相同类型的pair
    • 版本2:是个member template,在“构造过程中需要隐式类型转换”时被调用。如果
    • 版本3:隐式合成的,template形式的构造函数不会掩盖合成的拷贝构造函数。如果pair对象被复制,调用的是这个版本
  • 演示案例:
代码语言:javascript
复制
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构造函数,那么将不再编译成功。例如:
代码语言:javascript
复制
class A
{
public:
A(A&);
};


int main()
{
std::pair<A, int> p; //错误
return 0;
}

赋值运算符、移动语义

  • 从C++11起:
    • 赋值运算符以member template形式出现,使隐式类型转换得以进行
    • 此外,也支持move semantic(移动语义),可以搬移first和second元素。

四、元素访问

  • 因为pair被定义为struct,因此其所有成员都是public的,可以直接访问

first、second成员

  • first、second分别用来访问pair容器的第1、第2个数据成员
代码语言:javascript
复制
pair<string, string> author{ "James","Joyce" };
cout << author.first <<"\n"<< author.second << endl;
  • 例如自己实现一个泛型函数模板,可以将一个pair写入stream中
代码语言:javascript
复制
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
代码语言:javascript
复制
typedef std::pair<int, float> IntFloatPair;IntFloatPair p(42, 3.14);
std::get<0>(p); //等价于p.firststd::get<1>(p); //等价于p.second
std::tuple_size<IntFloatPair>::value;          //返回2std::tuple_element<0, IntFloatPair>::type num; //等价于int num

五、便捷函数make_pair()

make_pair()函数的定义

  • 在C++11之前,这个函数的声明和定义如下L
  • C++11之后,pair需要应付move semantic(移动语义),因此定义修改如下:

使用演示案例

  • 例如:
代码语言:javascript
复制
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时,确切的类型传递十分重要
代码语言:javascript
复制
std::pair<int, float>(42, 7.77);std::make_pair(42, 7.77);

移动语义(move semantic)

  • 如果你现在make_pair()中使用move semantic语义,那么你需要显式给出std::move()声明。例如:
代码语言:javascript
复制
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>提供)。例如:
代码语言:javascript
复制
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
代码语言:javascript
复制
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对象返回

演示案例:

代码语言:javascript
复制
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对象返回
代码语言:javascript
复制
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对象
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、pair历史概述
  • 二、pair概述
    • 特点:
      • 内部定义
        • pair的操作:
        • 三、构造函数、赋值、移动语义
          • 默认构造函数
            • 构造函数
              • 拷贝构造函数
                • 赋值运算符、移动语义
                • 四、元素访问
                  • first、second成员
                    • tuple-like接口
                    • 五、便捷函数make_pair()
                      • 使用演示案例
                        • 移动语义(move semantic)
                        • 六、std::tie()接口
                        • 七、pair作为函数返回值的使用
                          • 演示案例:
                          • 八、pair之间的比较
                          • 九、 pair运用实例
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档