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