std::map<std:: string,size_t> people {std::make_pair ("Ann",25),std::make_pair("Bill",46) , std::make_pair ("Jack",32), std::make_pair("Jill",32)}; auto pr = std::make_pair("Fred",22); //Create a pair element and insert it auto ret_pr = people.insert(pr); std::cout << ret_pr.first->first << " "<< ret_pr.first->second<< "" << std:: boolalpha <<ret_pr.second << "\n"; // Fred 22 true第一条语句生成了一个 map 容器,并用初始化列表中的 4 个值对它进行了初始化;在这种情况下,这些值会被隐式转换为要求的类型。第二条语句生成了另一个被插入的 pair 对象 pr。pr 对象的类型是 pair<const char*,int>,因为 make_pair<>() 函数模板的类型参数是从参数类型推断出来的;但是在 insert() 操作中,这个对象会被隐式转换为容器元素类型。当然,如果不想依靠隐式转换,可以生成所要求类型的 pair 对象:
auto pr = std::make_pair<std:: string, size_t> (std:: string { "Fred"},22);make_pair<>() 模板的显式类型参数决定了返回的 pair 对象的类型。可以把文字字符串作为第一个参数,然后通过隐式转换创建键需要的字符串对象。可以省略 make_pair<>() 的模板类型参数,让编译器去推断它们。假设像下面这样声明:
auto pr = std::make_pair ("Fred",22) ; // pair<const char*, int>这里会返回和所要求类型不同的 pair 对象。当允许编译器推断模板参数类型时,make_pair() 的参数可以准确地确定模板参数的类型。第一个参数是一个 const char* 类型的字符串,第二个参数是 int 类型。尽管已经说明了元素的类型,但在这种情况下,并没有多大的用处,因为在插入一个新元素时,pair 对象可以被隐式转换为容器所需类型。当 make_pair() 的参数的类型不能隐式转换成容器的键和对象的类型时,就需要注意了。
for (const auto& p : people) std::cout << std::setw(10) << std::left << p.first << " "<< p.second <<"\n";循环变量 p 通过引用依次访问 map 容器 people 中的每个元素。输出如下:
Ann 25
Bill 46
Fred 22
Jack 32
Jill 32
ret_pr = people.insert(std::make_pair("Bill", 48)); std:: cout << ret_pr.first->first <<" "<<ret_pr.first->second<< " "<<std::boolalpha<<ret_pr.second << "\n"; // Bill 46 false程序会输出如注释所示的内容。insert() 返回了一个 pair 对象 ret_pr,它的成员变量 first 指向 map 中已有的和键匹配的元素,成员变量 second 为 false,表明元素没有插入成功。
if(!ret_pr.second) // If the element is there change the age ret_pr.first—>second = 48;当键已经存在于 map 容器中时,ret_pr 的成员变量 second为false,所以这段代码会将 map 中这个元素的成员变量 second 的值设为 48。
ret_pr = people.insert(std::pair<const std::string, size_t> {"Bill", 48});这里会调用一个具有右值引用参数的 insert() 版本,所以假如元素不在容器中,那么它会被移到容器中。
auto ret_pr = people.insert(std::make_pair("Jim", 48)); people.insert (ret_pr.first, std::make_pair ("Ian", 38));第一条语句插入了一个元素,并像前面那样返回了一个对象。pair 对象的成员变量 first 是一个指向被插入的元素或容器中与插入元素有相同键的元素的迭代器。
if (!people.count("Ian")) people.insert (ret_pr.first, std::make_pair ("Ian", 38));只有当 count() 函数返回 0 时,insert() 才会被调用,这说明“Ian”键不在 map 中。当然,在不用提示插入元素时,需要做一次这样的检查,但 insert() 的返回值不管怎样都能告诉我们插入结果。
std::map<std::string, size_t> crowd {{"May", 55}, {"Pat",66}, {"Al", 22}, {"Ben", 44}}; auto iter = std::begin(people); std::advance(iter, 4); // begin iterator+ 4 crowd.insert(++std::begin(people),iter); // Insert 2nd, 3rd, and 4th elements from people这里生成了一个新的 map 容器 crowd,它有 4 个初始元素。iter 被初始化为 people 的开始迭代器。map 容器的迭代器是双向的,这样就可以对它们进行自增或自减,但是不能加上或减去一个值。这里使用了一个在前面章节中用过的 advance() 函数模板的实例来对 iter 加 4,所以它将会指向第 5 个元素,它在下一行被当作 crowd 的成员函数 insert() 的参数,用来作为指定元素段的结束迭代器。map 容器 people 的开始迭代器加 1,然后用它作为插入元素段的开始迭代器,所以会从 crowd 的第 2 个元素开始插入 3 个元素。
crowd.insert({{"Bert", 44}, {"Ellen”, 99}});这里会将初始化列表中的两个元素插入到 map 容器 crowd 中。参数表达式生成的 initializer_list<> 对象是 initializer_list<const string,size_t> 类型,因为编译器知道这时 insert() 函数的参数是这种类型。当然,也可以单独创建一个初始化列表,然后将它作为参数传递给 insert() 函数:
std::initializer_list<std::pair<const std:: string, size_t>>init {{"Bert", 44}, {"Ellen", 99}}; crowd.insert(init);initializer-list 模板的第一个类型参数必须为 const 类型。initializer_list <string,size_t> 无法隐式转换为 initializer_list<const string, size_t>,所以前者类型的对象不能作为 insert() 函数的参数。
// Defines a person's name #ifndef NAME_H #define NAME_H #include <string> // For string class #include <ostream> // For output streams #include <istream> // For input streams class Name { private: std::string first {}; std::string second {}; public: Name(const std::string& name1, const std::string& name2) : first (name1), second (name2) {} Name() = default; // Less-than operator bool operator<(const Name& name) const { return second < name.second || (second == name.second && first < name.first); } friend std::istream& operator>>(std::istream& in, Name& name); friend std::ostream& operator<<(std::ostream& out, const Name& name); }; // Extraction operator overload inline std::istream& operator>>(std::istream& in, Name& name) { in >> name.first >> name.second; return in; } // Insertion operator overload inline std::ostream& operator<<(std::ostream& out, const Name& name) { out << name.first + " " + name.second; return out; } #endif这个类非常简单,只有两个 string 类型的私有成员变量 first 和 second。这个构造函数可以接受 string 类型的参数或字符串常量参数。为了可以用这种对象作为 map 容器的键,必须为这个类定义 operator<()。为了便于对象的输入输出,也需要为流定义插入和提取操作。
using Entry = std::pair<const Name, size_t>;当容器是 map<name;site_t> 类型时,我们只能用 Entry 作为容器元素的类型。为了便于 map 元素的输出,我们可以把别名放到一个函数的定义中:
Entry get_entry() { std::cout << "Enter first and second names followed by the age: "; Name name {}; size_t age {}; std::cin >> name >> age; return make_pair(name, age); }从 cin 先后读入了一个 Name 对象和一个年龄值,并用它们生成了一个 pair 对象。从输入中读取 name 激发了定义在 Name.h 中的 istream 对象的重载函数 operator>>(),同样也支持流对象对 Name 对象的读入。
void list_entries(const map<Name, size_t>& people) { for(auto& entry : people) { std::cout << std::left << std::setw(30) << entry.first<< std::right << std::setw(4) << entry.second << std::endl; } }这里只用了基于范围的 for 循环来对元素进行遍历。循环变量 entry 依次引用 map 的每个元素。每一个 map 元素都是一个 pair 对象,它的成员变量 first 是 Name 类型的对象,成员变量 second 是 size_t 类型的值。
// Storing names and ages #include <iostream> // For standard streams #include <iomanip> // For stream manipulators #include <string> // For string class #include <map> // For map container class #include <utility> // For pair<> & make_pair<>() #include <cctype> // For toupper() #include "Name.h" using std::string; using Entry = std::pair<const Name, size_t>; using std::make_pair; using std::map; // Create a map entry from input Entry get_entry() { std::cout << "Enter first and second names followed by the age: "; Name name {}; size_t age {}; std::cin >> name >> age; return make_pair(name, age); } // Output the elements in a map void list_entries(const map<Name, size_t>& people) { for(auto& entry : people) { std::cout << std::left << std::setw(30) << entry.first << std::right << std::setw(4) << entry.second << std::endl; } } int main() { map<Name, size_t> people {{{"Ann", "Dante"}, 25}, {{"Bill", "Hook"}, 46}, {{"Jim", "Jams"}, 32}, {{"Mark", "Time"}, 32}}; std::cout << "\nThe initial contents of the map is:\n"; list_entries(people); char answer {'Y'}; std::cout << "\nEnter a Name and age entry.\n"; while(std::toupper(answer) == 'Y') { Entry entry {get_entry()}; auto pr = people.insert(entry); if(!pr.second) { // It's there already - check whether we should update std::cout << "Key \"" << pr.first->first << "\" already present. Do you want to update the age (Y or N)? "; std::cin >> answer; if(std::toupper(answer) == 'Y') pr.first->second = entry.second; } // Check whether there are more to be entered std::cout << "Do you want to enter another entry(Y or N)? "; std::cin >> answer; } std::cout << "\nThe map now contains the following entries:\n"; list_entries(people); }定义一些额外的别名可以减少代码冗余。可以用 using::namespace 来完全消除对 std 名称限定的需要,但不赞成这么做,因为 std 中的所有命名都被有效导入到当前作用域内,这违背了定义命名空间的初衷。
The initial contents of the map is:
Ann Dante 25
Bill Hook 46
Jim Jams 32
Mark Time 32
Enter a Name and age entry.
Enter first and second names followed by the age: Emma Nate 42
Do you want to enter another entry(Y or N)? y
Enter first and second names followed by the age: Emma Nate 43
Key "Emma Nate" already present. Do you want to update the age (Y or N)? Y
Do you want to enter another entry(Y or N)? y
Enter first and second names followed by the age: Eamonn Target 56
Do you want to enter another entry(Y or N)? n
The map now contains the following entries:
Ann Dante 25
Bill Hook 46
Jim Jams 32
Emma Nate 43
Eamonn Target 56
Mark Time 32
Copyright © 广州京杭网络科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有