1Pair
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> 对象。
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"})};
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 成员变量的类型,因此可以成功赋值。如果这些类型不能隐式转换,这条赋值语句就无法通过编译。
pair 对象有全套的运算符 ==、!=、<、<=、>、>=.
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。
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;
pair 的成员函数 swap() 可以和作为参数传入的另一个 pair 对象交换其成员变量 first 和 second。显然,参数必须是相同类型。
std::pair<int, int> p1 {10, 11};
std::pair<int, int> p2 {11, 9};
p1.swap(p2); // p1={ll,9} p2={10/11}
如果执行两次 swap(),对象恢复成原来的值。
当需要将多个对象当作一个对象传给函数时,tuple 类型是很有用的。
make_tuple()函数可以接受不同类型的任意个数的参数,返回的 tuple 的类型由参数的类型决定。
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
可以用任何比较运算符来比较相同类型的 tuple 对象。tuple 对象中的元素是按照字典顺序比较的。
std::cout << std::boolalpha << (my_t4 < my_t5) << std::endl;
tuple 对象中的元素是依次比较的,第一个不同的元素决定了比较结果。my_t4 的第一个元素小于 my_t5 的第一个元素,因此比较结果为 true。如果是相等比较,任何一对不相等的对应元素都会使比较结果为 false。
my_t4.swap (my_t5);
通过调用成员函数 swap() 来交换 my_t4 和 my_t5 对应的元素。显然,tuple 中所有元素的类型都必须是可交换的。
tuple 中的对象数目是不固定的,所以访问它们的机制必须能够满足这种情况。函数模板 get<>() 可以返回 tuple 中的一个元素.
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;
输出结果是:
Peter Piper age = 42 tel: 914 626 7890
可以用基于类型的 get<>() 从 tuple 获取元素,但要求 tuple 中只有一个这种类型的元素。
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<>() 的左值集合。
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 的值:
std::tie(name, std::ignore,phone) = my_tuple;
ignore 定义在 tuple 中,它被用来标记 tie() 函数中要被忽略的值.
也可以用 tie() 函数来实现对类的数据成员的字典比较。在 Name 类中实现 operator<() 函数:
bool Name::operator<(const Name& name) const{
return std::tie(second, first) < std::tie(name.second, name.first);}
multimap 容器保存的是有序的键/值对,但它可以保存重复的元素。multimap 中会出现具有相同键的元素序列,它们会被添加到容器中。
multimap 容器的成员函数 insert() 可以插入一个或多个元素,而且插入总是成功。返回一个指向插入元素的迭代器。
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(),可以通过为这个函数提供一个迭代器形式的提示符来控制元素的生成位置:
auto iter = pets.emplace("rabbit”,"Flopsy");
iter = pets.emplace_hint (iter, "rabbit", "Mopsy");// Create preceding Flopsy
这两个函数都返回一个指向插入元素的迭代器。emplace_hint() 函数尽可能近地在第一个参数所指向位置的前面生成一个新元素。如果只使用 emplace() 来插入 "Mopsy",它可能会被插入到当前所有键为 "rabbit" 的元素的后面。
multimap 不支持下标运算符,因为键并不能确定一个唯一元素。和 map 相似,multimap 也不能使用 at() 函数。
multimap 的成员函数 fmd() 可以返回一个键和参数匹配的元素的迭代器。
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 对象,这两个迭代器所确定范围内的元素的键和参数值相等。
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() 返回的迭代器是相同的。因而前面的代码段可以这样重写:
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()知道有多少个元素的键和给定的键相同。
auto n = people.count("Jack"); // Returns 2
应用:
#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;
}
}
结果显示:
输出表明元素是按键的升序排列的,键相同的元素的顺序和它们输入的顺序相反。