日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

通用工具之Pair和Tuple-《C++标准库(第二版)》读书笔记

發布時間:2025/4/5 c/c++ 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通用工具之Pair和Tuple-《C++标准库(第二版)》读书笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面:本文是閱讀《C++標準庫(第二版)》的讀書筆記。

文章目錄

    • 5.1 Pair 和Tuple
      • 5.1.1 Pair
        • 元素訪問
        • 構造函數和賦值
        • 逐塊式構造
        • 便捷函數make_pair()
        • Pair 之間的比較
      • 5.1.2 Tuple(不定數的值組)
        • Tuple的操作
        • 便捷函數make_tuple()和tie()
        • Tuple和初值列

5.1 Pair 和Tuple

5.1.1 Pair

Class pair 可將兩個value 視為一個單元。C++標準庫中多處用到了這個class。尤其容器map、multimap,unordered_map和unordered_multimap就是使用pair來管理其以key/value pair形式存在的元素。 任何函數如果需要返回兩個value,也需要用到pair,例如minmax().

struct pair 定義于< utility> ,下表中的各項操作它都支持。原則上可以對pair< > 執行create、copy/assign/swap 及compare操作 。此外它還提供first_type和second_type 類型定義式,用來表示第一value和第二value的類型。

元素訪問

為了讓程序能夠處理pair 的兩個值,它提供了”直接訪問對應數據成員“的能力。事實上,由于它是個struct 而不是class,以至于所有成員都是public:

namespace std{template<typename T1, typename T2>struct pair {//memberT1 first;T2 second;...}; }

舉個例子,想要實現一個泛型函數模板,用以將一個value pair 寫入一個stream內,必須這樣做:

template <typename T1, typename T2> std:: ostream& operator<< (std::ostream & strm,const std::pair<T1,T2>& p) {return strm <<"["<< p.first<<"," << p.second <<"]"; }

另外,自C++11起,可以對pair使用一份tuple-like 接口。因此,你可以使用tuple_size< > ::value 獲得元素個數,使用tuple_element < > ::type 獲得某指定元素的類型,也可以使用get() 獲得first 或second:

typedef std::pair<int,float> IntFloatPair;IntFloatPair p(42,3.14);std::get<0>(p) // yields p.first std::get<1>(p) // yields p.second std::tuple_size<IntFloatPair> ::value //yields 2 std::tuple_element<0,IntFloatPair>::type //yields int

構造函數和賦值

Default構造函數生成一個pair時,以兩個”被default構造函數個別初始化“的元素作為初值。根據語言規則,基礎類型(如int)的default析構函數也可以引起適當的初始化動作,所以

std::pair<int,float> p; //initialize p.first and p.second with zero

就是以int() 和float() 來初始化p。 這兩個構造函數都傳回零值。

Copy構造函數同時存在兩個版本,版本1接受相同類型的pair,版本2是個member template,在”構造過程中需要隱式類型轉換“時被調用。如果pair對象被復制,調用的是被隱式合成的那個copy構造函數。 例如

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); //OK:calls implicitly generated copy constructorg(p); //OK:calls template constructor }

逐塊式構造

Class pair< > 提供三個構造函數,用以初始化first 和second成員:

namespace std{template <typename T1, typename T2>struct pair{...pair(const T1&x ,const T2& y);template<typename U, typename V> pair(U&& x, V&& y);template<typename ... Args1,typename ... Args2>pair( piecewise_construct_t,tuple<Args1...> first_args,tuple<Args2...> second_args);...}; }

前兩個構造函數提供的是慣常行為:傳遞一個實參給first,另一個實參給second,并且涵蓋對move semantic 和隱式類型轉換的支持。第三個構造函數有點特別,它允許傳遞兩個tuple(那是一種”擁有不定個數的元素且元素類型各不相同“的對象),但以另外一種方式處理它們。正常而言,如果傳遞1個或2個tuple,最前面兩個構造函數允許初始化一個pair,其first和/或 second是tuple。但是第三個構造函數使用tuple,將其元素傳遞給first和second的構造函數。為了強迫執行這樣的行為,你必須傳遞std::piecewise_construct 作為額外的第一實參。

舉個例子

#include<bits/stdc++.h> using namespace std;class Foo{public: Foo(tuple<int,float>){cout<<"Foo::Foo(tuple)"<<endl;}template <typename ...Args>Foo(Args... args){cout<<"Foo::Foo(args...)"<<endl;} }; int main(){//create tuple t:tuple<int,float> t(1,2.22);//pass the tuple as a whole to the constructor of Foo:pair<int,Foo> p1(42, t);// pass the elements of the tuple to the constructor of Foo:pair<int,Foo> p2(piecewise_construct,make_tuple(42),t); }

執行結果

只有當std::piecewise_construct被當作第一實參時,class Foo 才會被迫使用那個”接受tuple的元素(一個int和一個float)而非tuple整體“的構造函數。這意味著,此例中被調用的是Foo那個”實參數量不定的構造函數“。如果提供了Foo::Foo(int ,float)的話,被調用的將是它。

如你所見,兩個實參都必須是tuple才會強迫導致這個行為。因此,第一實參42被顯式轉化為一個tuple,用的是make_tuple().

便捷函數make_pair()

Template函數make_pair()使你無需寫出類型就能生成一個pair對象。舉個例子,不用這樣寫;

std::pair<int,char>(42,'@')

可以這樣寫

std::make_pair(42,'@')

自C++11起,make_pair()聲明如下

namespace std{//create value pair only by providing the valuestemplate<template T1, template T2>pair<V1,V2> make_pair(T1&&x,T2&& y); }

其中返回值的細節和它們的類型V1,V2 ,取決于x和y的類型。標準明確指出,如果可能的話make_pair()使用move語義,否則就是用copy語義。 此外,它會蛀蝕(decay)實參,致使make_pair(“a”,“xy”)產生一個pair< const char* ,const char*> 而非一個 pair< const char[2], const char[3] >

當我們必須對一個”接受pair為實參“的函數傳遞兩個value時,make_pair()就會特別方便,請看下列:

void f(pair<int,const char*>); void g(pair<const int,string>);void foo(){f(make_pair(42,"empty")); //pass 2 values as pairg(make_pair(42,"chair")); //pass 2 values as pair with type conversions }

從本例可以看出,make_pair()即使在類型并不準確吻合的情況下也能借由template構造函數提供的支持順利運行。當你使用map和multimap時,經常需要這樣的能力。

注意,自C++11開始,可以使用初值列

f({42,"empty"}); g({42,"chair"});

然而一個表達式如果明白指出類型,便帶有一個優勢:產生出來的pair將有絕對明確的類型。例如

pair<int, float>(42,7.77)

所得到的結果和下面不同

make_pair(42,7.77)

后者所產生的pair的第二個元素的類型是double(因為無任何限定符的浮點字面常量的類型被視為double)。當我們使用重載函數或template的時候,確切的類型非常重要。例如,為了提高效能,程序員可能同時提供分別針對float和double的重載函數或template,這時候確切的類型就非常重要。

Pair 之間的比較

為了比較兩個pair對象,C++標準庫提供了大家慣用的操作符。兩個pair對象內的所有元素都相等,這兩個pair對象才被視為相等。

namespace std{template<typename T1,typename T2>bool operator ==(const pair<T1,T2> &x, const pair<T1,T2> &y){return x.first==y.first && x.second==y.second;} }

兩個pair相互比較時,第一元素具有較高的優先級。所以如果兩個pair的第一元素不相等,其比較結果就成為整個比較的結果。如果first相等,才繼續比較second,并把比較結果當作整體結果

namespace std{template<typename T1,typename T2>bool operator< (const pair<T1,T2>&x, const pair<T1,T2> &y){return x.first <y.first ||(!(y.first< x.first) && x.second < y.second);} }

5.1.2 Tuple(不定數的值組)

Tuple 擴展了pair的概念,擁有任意數量的元素。也就是說,tuple呈現出一個異質元素列,其中每個類型都可以被指定,或來自編譯期推導。

C++11,variadic template 被引入進來,使template得以接受任何數量的template實參,于是,出現在< tuple> 中的class tuple 聲明式 現在就被簡化為如下:

namespace std{template< typename ... Types>class tuple; }

Tuple的操作

原則上,tuple接口十分直觀:

  • 通過明白的聲明,或使用便捷函數make_tuple(),你可以創建一個tuple。
  • 通過get<>() function template,你可以訪問tuple的元素。

下面是其接口的一個基本示例

#include<bits/stdc++.h> using namespace std;int main(){tuple<string,int, int ,complex<double>> t;//create adn initialize a tuple explicitlytuple<int,float,string> t1(41, 6.3, "nico");cout<< get<0> (t1)<<" ";cout<< get<1> (t1)<<" ";cout<<get<2> (t1) <<" ";cout<<endl;//create tuple with make_tuple()auto t2 = make_tuple(22,43.1,"hello");//assign second value in t2 to t1get<1>(t1) = get<1>(t2);cout<< get<1> (t1)<<endl;if(t1< t2)t1=t2;return 0; }

測試結果


下面是分析

tuple<string,int, int ,complex<double>> t;

該條語句建立起了一個異質的四元素tuple,每個元素的內容由default構造函數初始化。基礎類型都被初始化為0.

下面的語句 tuple<int, float, string> t1(41, 6.3 ,“nico”); 建立并初始化一個異質的三元素tuple。

你也可以使用make_tuple()建立tuple ,其所有元素類型都自動推導自它的初值。
注意, tuple的元素類型可以是reference。例如

string s; tuple<string&> t(s); // first element of tuple t refers to sget<0> (t) ="hello"; //assigns "hello" to s

Tuple不是尋常的容器,不允許迭代元素。對于tuple可以使用其成員函數來處理元素,因此必須在編譯期知道你打算處理的元素的索引值。 例如你可以這樣處理tuple t1 的第一元素:

get<0>(t1)

運行期才傳入一個索引值是不被允許的:

int i; get<i> (t1) // compile-time error: i is no compile-time value

好消息是,萬一傳入無效索引,編譯器會報錯:

get<3> (t1) //compile-time error if t1 has only three elements

此外,tuple 還提供常用的拷貝,賦值和比較操作,它們身上都允許發生隱式類型轉換(因為采用member template),但元素個數必須絕對吻合。 如果兩個tuple 的所有元素都相等,它們就整體相等。檢查某個tuple是否小于另一個tuple,采用的是lexicographical(字典序)比較法則。

便捷函數make_tuple()和tie()

make_tuple()函數會根據value建立tuple,不需要明確指出元素的類型。

auto t2 = make_tuple(22,43.1,"hello");

這句話建立并初始化了一個tuple,其對應的三個元素類型是int ,double 和 const char* 。

借用特別的函數對象reference_wrapper < > 及 便捷函數 ref() 和 cref() ,可以影響make_tuple()產生的類型,例如以下表達式產出的tuple 帶有一個reference 指向變量或對象s:

string s; make_tuple(ref(s)); //yields type tuple<string&> ,where the element refers to s

如果你打算改動tuple內的一個既有值,上述就很重要:

std::string s;auto x= std:: make_tuple(s); // x is of type tuple <string>std:: get<0>(x) = "my values"; // modifies x but not sauto y =std::make_tuple(ref(s)); // y is of type tuple<string&>, thus y refers to sstd::get<0> (y) = "my values"; //modifies y

運用reference搭配make_tuple() ,就可以提取tuple的元素值,將某些變量值設給它們,例如以下例子:

tuple<int, float, string> t(77, 1.1, "more light"); int i; float f;string s;//assign values of t to i, f and s: make_tuple( ref(i), ref(f), ref(s))=t;

然后分別輸出 i,f,s

如果想最方便地在tuple中使用reference,可選擇tie(),它可以建立一個內含reference的tuple:

tuple<int,float, string> t(77,1.1, "more light"); int i; float f; string s; tie(i,f,s)=t; //assigns values of t to i , f and s

這里的tie(i,f,s)=t 會以i,f,s的reference 建立起一個tuple,因此上述賦值操作其實就是將t內的元素分別賦值為i,f和s。

使用tie()時, ignore允許我們忽略tuple的某些元素,也就是我們可以用它來局部提取tuple 的元素值:

tuple<int,float, string> t(77,1.1, "more light"); int i; string s; tie(i,std::ignore,s)=t; //assigns first and third value of t to i and s

Tuple和初值列

不可以使用賦值語法將某個tuple初始化,因為那會被視為一個隱式轉換

tuple<int ,double> t1(42,3.14); // OK ,old syntax tuple<int,double> t2{42, 3.14};//OK: new syntax tuple<int, double> t3={42, 3.14}; //ERROR

此外,你不可以將初值列傳到“期望獲得一個tuple”的地方:

vector< tuple<int,float>> v{{1,1.0},{2, 2.0}};//ERRORtuple<int,int,int> foo(){return {1, 2, 3};//ERROR }

注意,上述做法對于pair< > 和容器(除了array < >外) 是行得通的:

vector< pair<int, float> > v1{{1,1.0},{2, 2.0}}; //OK vector<vector<float>> v2{{1,1.0},{2, 2.0}}; //OKvector<int> foo2(){return {1, 2, 3}; //OK }

但是對于tuple ,必須明確地將初值轉換為一個tuple(比如運用make_tuple()):

vector< tuple<int,float>> v{ make_tuple(1,1.0),make_tuple(2, 2.0)};//OKtuple<int,int,int> foo(){return make_tuple(1, 2, 3);//OK }

總結

以上是生活随笔為你收集整理的通用工具之Pair和Tuple-《C++标准库(第二版)》读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。