前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STL之关联式容器(pair,tuple和multimap)

STL之关联式容器(pair,tuple和multimap)

作者头像
用户9831583
发布2022-06-16 14:43:09
5010
发布2022-06-16 14:43:09
举报
文章被收录于专栏:码出名企路

1Pair

1.1初始化
代码语言:javascript
复制
std::string s1 {"test”}, s2{"that"};
std::pair<std::string, std::string> my_pair{s1, s2};
std::pair<std::string, std::string> your_pair{std::string {"test"},std::string {"that"}};
std::pair<std::string, std::string> his_pair{"test", std::string {"that"}};
std::pair<std::string, std::string> her_pair{"test", "that"};

make_pair<T1,T2> 函数模板是一个辅助函数,可以生成并返回一个 pair<T1,T2> 对象。

代码语言:javascript
复制
auto my_pair = std::make_pair(s1, s2);
auto your_pair = std::make_pair(std::string {"test"},std::string {"that"});
auto his_pair = std::make_pair<std::string, std::string>("test",std::string {"that"});
auto her_pair = std::make_pair<std::string, std::string>("test", "that");

pair 对象也可以复制或移动构造它的成员变量

std::pair<std::string, std:: string> new_pair{my_pair}; // Copy constructor

std::pair<std::string, std::string> old_pair{std::make_pair(std::string{"his"},std::string{"hers"})};

代码语言:javascript
复制
auto prl = std::make_pair ("these", "those"); // Type pair<const char*, const char*>
std::pair<std::string, std::string> pr2; // Type pair<string, string>
pr2 = prl; // OK in this case

prl 成员变量 first 和 second 的类型是 const char*。这个类型可以隐式转换为 string,即 pr2 成员变量的类型,因此可以成功赋值。如果这些类型不能隐式转换,这条赋值语句就无法通过编译。

1.2比较大小

pair 对象有全套的运算符 ==、!=、<、<=、>、>=.

代码语言:javascript
复制
std::pair<std::string, std::string> new_pair;
new_pair.first = "his";
new_pair.second = "hers";
if (new_pair == std::pair<std::string, std::string> {"his", ,"hers"})    
std::cout << "Equality!\n"; 

对于小于或大于比较,pair 对象的成员变量是按字典顺序比较的。如果 new_pair.first 小于 old_pair.first 的话,表达式 new_pair<old_pair 会返回 true。如果它们的成员变量 first 相等,但 new_pair.second 小于 old_pair.second,new_pair < old_pair 也为 true。

代码语言:javascript
复制
std::pair<int, int> p1 {10, 9};
std::pair<int, int> p2 {10, 11};
std::pair<int, int> p3 {11, 9};
std::cout<<std::boolalpha << (p1 < p2) <<" "<<(pi > p3) << " "<< (p3 > p2) << std::endl;
1.3交换元素

pair 的成员函数 swap() 可以和作为参数传入的另一个 pair 对象交换其成员变量 first 和 second。显然,参数必须是相同类型。

代码语言:javascript
复制
std::pair<int, int> p1 {10, 11};
std::pair<int, int> p2 {11, 9};
p1.swap(p2); // p1={ll,9} p2={10/11}

如果执行两次 swap(),对象恢复成原来的值。

2.tuple

当需要将多个对象当作一个对象传给函数时,tuple 类型是很有用的。

2.1初始化

make_tuple()函数可以接受不同类型的任意个数的参数,返回的 tuple 的类型由参数的类型决定。

代码语言:javascript
复制
auto my_tuple = std::make_tuple (Name{"Peter”,"Piper"},42,std::string{"914 626 7890"});

tuple 对象的构造函数提供了可能会用到的每一种选择。例如:

std::tuple<std::string, size_t> my_tl;//Default initialization

std::tuple<Name,std::string>my_t2{Name{"Andy", "Capp"},std::string{“Programmer”}};

std::tuple<Name,std::string> copy_my_t2{my_t2}; // Copy constructor

std::tuple<std::string, std::string> my_t3 {"this", "that"};// Implicit conversion

2.2比较大小

可以用任何比较运算符来比较相同类型的 tuple 对象。tuple 对象中的元素是按照字典顺序比较的。

代码语言:javascript
复制
std::cout << std::boolalpha << (my_t4 < my_t5) << std::endl;

tuple 对象中的元素是依次比较的,第一个不同的元素决定了比较结果。my_t4 的第一个元素小于 my_t5 的第一个元素,因此比较结果为 true。如果是相等比较,任何一对不相等的对应元素都会使比较结果为 false。

2.3交换元素
代码语言:javascript
复制
my_t4.swap (my_t5);

通过调用成员函数 swap() 来交换 my_t4 和 my_t5 对应的元素。显然,tuple 中所有元素的类型都必须是可交换的。

2.4访问元素

tuple 中的对象数目是不固定的,所以访问它们的机制必须能够满足这种情况。函数模板 get<>() 可以返回 tuple 中的一个元素.

代码语言:javascript
复制
auto my_tuple = std::make_tuple (Name {"Peter","Piper"}, 42, std::string {"914 626 7890"});
std::cout << std::get<0>(my_tuple)<< "age = "<<std::get<1>(my_tuple)<< " tel: " << std::get<2>(my_tuple) << std::endl;

输出结果是:

代码语言:javascript
复制
Peter Piper age = 42 tel: 914 626 7890

可以用基于类型的 get<>() 从 tuple 获取元素,但要求 tuple 中只有一个这种类型的元素。

代码语言:javascript
复制
auto my_tuple = std::make_tuple(Name{"Peter", "Piper"}, 42, std::string {"914 626 7890"});
std::cout << std::get<Name>(my_tuple)<<" age = " << std::get<int> (my_tuple)<< " tel: " <<std::get<std::string>(my_tuple) << std::endl;

如果 tuple 中包含的 get<>() 类型参数值的元素不止一个,代码就无法编译通过。

2.5tie<>()

全局的 tie<>() 提供了另一种访问 tuple 元素的方式。可以把 tuple 中的元素值转换为可以绑定到 tie<>() 的左值集合。

代码语言:javascript
复制
auto my_tuple = std::make_tuple(Name{"Peter","Piper"}, 42, std::string{"914 626 7890"});
Name name{};
size_t age{};
std::string phone{};
std::tie(name, age, phone) = my_tuple;

不想存储每一个元素的值。只保存 my_tuple 中 name 和 phone 的值:

代码语言:javascript
复制
std::tie(name, std::ignore,phone) = my_tuple;

ignore 定义在 tuple 中,它被用来标记 tie() 函数中要被忽略的值.

也可以用 tie() 函数来实现对类的数据成员的字典比较。在 Name 类中实现 operator<() 函数:

代码语言:javascript
复制
bool Name::operator<(const Name& name) const{  
return std::tie(second, first) < std::tie(name.second, name.first);}

3multimap

multimap 容器保存的是有序的键/值对,但它可以保存重复的元素。multimap 中会出现具有相同键的元素序列,它们会被添加到容器中。

3.1插入元素

multimap 容器的成员函数 insert() 可以插入一个或多个元素,而且插入总是成功。返回一个指向插入元素的迭代器。

代码语言:javascript
复制
std::multimap<string, string〉 pets; // Element is pair{pet_type, pet_name}
auto iter = pets.insert (std::pair<string, string>{string{"dog"}, string{"Fang"}});
iter = pets.insert(iter, std::make_pair("dog", "Spot")); // Insert Spot before Fangpets.insert(std::make_pair("dog", "Rover"));// Inserts Rover after Fang
pets.insert (std::make_pair ("cat", "Korky"));// Inserts Korky before all dogs
pets.insert ({{ "rat", "Roland"}, {"pig", "Pinky" }, {"pig", "Perky"}});//Inserts list elements

emplace() 可以在容器的适当位置构造元素。在插入具有相同键的元素时,可以使用 multimap 的成员函数 emplace_hint(),可以通过为这个函数提供一个迭代器形式的提示符来控制元素的生成位置:

代码语言:javascript
复制
auto iter = pets.emplace("rabbit”,"Flopsy");
iter = pets.emplace_hint (iter, "rabbit", "Mopsy");// Create preceding Flopsy

这两个函数都返回一个指向插入元素的迭代器。emplace_hint() 函数尽可能近地在第一个参数所指向位置的前面生成一个新元素。如果只使用 emplace() 来插入 "Mopsy",它可能会被插入到当前所有键为 "rabbit" 的元素的后面。

3.2访问元素

multimap 不支持下标运算符,因为键并不能确定一个唯一元素。和 map 相似,multimap 也不能使用 at() 函数。

multimap 的成员函数 fmd() 可以返回一个键和参数匹配的元素的迭代器

代码语言:javascript
复制
std::multimap<std::string, size_t> people {{"Ann",25},{"Bill", 46}, {"Jack", 77}, {"Jack", 32},{"Jill", 32}, {"Ann", 35} };
std::string name {"Bill"};
auto iter = people.find(name);
if (iter ! = std::end (people))    
std::cout << name << " is " << iter->second << std::endl;
iter = people.find ("Ann");
if (iter != std::end(people))  
std::cout << iter->first << " is " << iter->second <<std::endl;

如果没有找到键,会返回一个结束迭代器,所以我们应该总是对返回值进行检查。

第一个 find() 调用的参数是一个键对象,因为这个键是存在的,所以输出语句可以执行。

第二个 find() 调用的参数是一个字符串常量,它说明参数不需要和键是相同的类型。最后一条输出语句也可以执行,因为有等于 "Ann" 的键。

如果使用 multimap 容器,几乎可以肯定它会包含键重复的元素;否则,就应该使用 map。

一般来说,我们想访问给定键对应的所有元素。 equal_range() 。它会返回一个封装了两个迭代器的 pair 对象,这两个迭代器所确定范围内的元素的键和参数值相等。

代码语言:javascript
复制
auto pr = people.equal_range("Ann");
if(pr.first != std::end(people)){  
for (auto iter = pr.first ; iter != pr.second; ++iter)      
std:cout << iter->first << " is " << iter->second << std::endl;}

equal_range() 的参数可以是和键同类型的对象,或是不同类型的但可以和键比较的对象。返回的 pair 对象的成员变量 first 是一个迭代器,它指向第一个大于等于参数的元素;如果键和参数相等的元素存在的话,它是第一个键和参数相同的元素。如果键不存在,pair 的成员变量 first 就是容器的结束迭代器.

lower_bound() 会返回一个迭代器,它指向键值和参数相等或大于参数的第一个元素,或者指向结束迭代器。upper_bound() 也返回一个迭代器,它指向键值大于函数参数的第一个元素,如果这样的元素不出现的话,它就是一个结束迭代器。

所以,当存在一个或多个相等键时,这些函数会返回一个开始迭代器和一个结束迭代器,它们指定了和参数匹配的元素的范围,这和 equal_range() 返回的迭代器是相同的。因而前面的代码段可以这样重写:

代码语言:javascript
复制
auto iter1 = people.lower_bound("Ann");
auto iter2 = people.upper_bound("Ann");
if(iter1 != std::end(people)){  
for(auto iter = iterl ; iter != iter2; ++iter)      
std::cout << iter->first << " is " << iter->second << std::endl;}

它和前一个代码段的输出结果是相同的。

count()知道有多少个元素的键和给定的键相同。

代码语言:javascript
复制
auto n = people.count("Jack"); // Returns 2

应用:

代码语言:javascript
复制
    #include <iostream>                                        
    #include <string>                                          
    #include <map>                                          
    #include <cctype>   // For toupper()
    using std::string;
    using Pet_type = string;
    using Pet_name = string;
    int main()
    {
        std::multimap<Pet_type, Pet_name> pets;
        Pet_type type {};
        Pet_name name {};
        char more {'Y'};
        while(std::toupper(more) == 'Y')
        {
            std::cout << "Enter the type of your pet and its name: ";
            std::cin >> std::ws >> type >> name;
            //将给定键的第二个以及随后的元素插入到这个键序列的前面
            auto iter = pets.lower_bound(type);
            if(iter != std::end(pets))
                pets.emplace_hint(iter, type, name);
            else
                pets.emplace(type, name);
            std::cout << "Do you want to enter another(Y or N)? ";
            std::cin >> more;
        }
        // 首先找到 iter 指向的 pet 的第一个类型,然后用 equal_range()
        //返回的迭代器列出这种 pet 类型的全部序列。最后将 iter 设为这个序列的结束迭代器
        std::cout << "\nPet list by type:\n";
        auto iter = std::begin(pets);
        while(iter != std::end(pets))
        {
            auto pr = pets.equal_range(iter->first);
            std::cout << "\nPets of type " << iter->first << " are:\n";
            for(auto p = pr.first; p != pr.second; ++p)
                std::cout << "  " << p->second;
            std::cout << std::endl;
            iter = pr.second;
        }
    }

结果显示:

输出表明元素是按键的升序排列的,键相同的元素的顺序和它们输入的顺序相反

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码出名企路 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1初始化
  • 1.2比较大小
  • 1.3交换元素
  • 2.tuple
    • 2.1初始化
      • 2.2比较大小
        • 2.3交换元素
          • 2.4访问元素
          • 3multimap
            • 3.1插入元素
              • 3.2访问元素
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档