淺析C++標(biāo)準(zhǔn)庫元組(tuple)源碼
一、什么是元組
元組不是什么新鮮東西,在數(shù)學(xué)、python語言還有我們今天要說的C++都有元組。
簡(jiǎn)單地說,元組就是一組東西,例如,在講代數(shù)拓?fù)涞臅r(shí)候,經(jīng)常把拓?fù)淇臻gX和其中一點(diǎn)x作為一個(gè)偶對(duì)(X, x),這其實(shí)就是個(gè)元組,點(diǎn)的坐標(biāo)也可以看成一個(gè)元組。C++中的元組(tuple)是這個(gè)樣子的:
std::tuple<int, std::string> tu{ 2,"12iop" };
一個(gè)tuple可以包含不同類型的成員,例如上面的tu包含一個(gè)int和一個(gè)字符串。
二、用法
在考察源碼之前,我們必須先知道它的用法。
要想使用tuple,要包含頭文件<tuple>:
#include <tuple>
tuple實(shí)際上是一個(gè)有可變參數(shù)的類模板,使用的時(shí)候,傳入若干個(gè)參數(shù)將其特化。
struct Point { int x; int y; }; void main() { std::tuple<int, std::string> t1{ 1,"qwer" }; // 一個(gè)由int和字符串組成的tuple constexpr std::tuple<int, void*> t2{ 1,nullptr }; // 一個(gè)由int和void*組成的tuple std::tuple<int, Point> t3{ 1,{20,89} }; // 一個(gè)由int和Point結(jié)構(gòu)體組成的tuple std::tuple<int, char, std::string> t4{ 1,'t',"qwer" }; // 一個(gè)由int、char、字符串組成的tuple }
上面的代碼中,我用constexpr修飾了t2,這是完全正確的,std::tuple的構(gòu)造函數(shù)是constexpr的。
獲取tuple中的值,用std::get。這不是函數(shù),而是函數(shù)模板,我們需要傳入size_t類型的變量將其特化,或者傳入一個(gè)類型,告訴它我們需要取出元組中的哪個(gè)類型的成員。
struct Point { int x; int y; }; void main() { std::tuple<int, std::string> t1{ 1,"qwer" }; constexpr std::tuple<int, void*> t2{ 10,nullptr }; std::tuple<int, Point> t3{ 1,{20,89} }; std::tuple<int, char, std::string> t4{ 1,'t',"qwer" }; std::cout << std::get<0>(t1) << std::endl; // 1 constexpr int n2 = std::get<0>(t2); std::cout << n2 << std::endl; // 10 auto s = std::get<char>(t4); std::cout << s << std::endl; // t }
std::get也是constexpr的,所以n2也是一個(gè)編譯時(shí)的常量。
我們通過get<char>的方式得到了s,它是char類型的變量。std::get<T>可以從tuple中獲取到第一個(gè)類型為T的成員。
tuple也可以用【==】和【!=】比較是否相等:
std::tuple<int, std::string> t5{ 1,"qwer" }; if (t1 == t5) { std::cout << "==" << std::endl; }
介紹tuple的用法不是本文的主要內(nèi)容,故到此為止。有興趣的同學(xué)可以自行查閱資料。
接下來,是時(shí)候考察一看源碼了。
三、源碼分析
tuple是個(gè)可變參數(shù)的類模板:
template<typename... _Types> class tuple;
這是對(duì)類模板的聲明。
接下來,實(shí)現(xiàn)參數(shù)個(gè)數(shù)為零的空tuple。
3.1 tuple<>
struct allocator_arg_t {}; template<> class tuple<> { public: typedef tuple<> _Myt; constexpr tuple() noexcept {} template<typename _Alloc> tuple(allocator_arg_t, const _Alloc&) noexcept {} constexpr tuple(const tuple&) noexcept {} template<class _Alloc> tuple(allocator_arg_t, const _Alloc&, const _Myt&) noexcept {} void swap(_Myt&) noexcept {} constexpr bool _Equals(const _Myt&) const noexcept { return true; } constexpr bool _Less(const _Myt&) const noexcept { return false; } };
allocator_arg_t是個(gè)空的結(jié)構(gòu)體,暫時(shí)不管它。_Myt就是tuple<>自己,這樣寫起來方便一些。
tuple<>定義了空的構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)(空tuple沒什么可做的)。
成員函數(shù)swap用于與另一個(gè)tuple<>交換內(nèi)容,因?yàn)闆]什么可交換的,函數(shù)體當(dāng)然是空的。
_Equals用來判斷兩個(gè)tuple<>是否相等,它返回true,這是顯然的(所有的tuple<>都是一個(gè)樣子)。
_Less從函數(shù)名看,是為了比較大小,但如果遇到?jīng)]有重載<的類型呢?暫時(shí)不管它。
有了空tuple的定義,就可以定義非空的tuple。
3.2 非空的tuple
template<class _This, class... _Rest> class tuple<_This, _Rest...> : private tuple<_Rest...> { // 內(nèi)容 }
n(>0)個(gè)元素的tuple私有繼承了n-1個(gè)元素的tuple。顯然這是一種遞歸定義,最終會(huì)遞歸到tuple<>,而tuple<>是已經(jīng)定義好了得。
例如,tuple<int, char, short>私有繼承了tuple<char, short>,而tuple<char, short>又私有繼承了tuple<short>,tuple<short>私有繼承了tuple<>。由于私有繼承可以實(shí)現(xiàn)“has-a”功能,所以,這樣的方式可以將不同類型的對(duì)象組合在一起。如下圖:
那么,tuple是如何存儲(chǔ)其中的元素呢?
template<class _This, class... _Rest> class tuple<_This, _Rest...> : private tuple<_Rest...> { // recursive tuple definition public: typedef _This _This_type; typedef tuple<_This, _Rest...> _Myt; typedef tuple<_Rest...> _Mybase; static const size_t _Mysize = 1 + sizeof...(_Rest); _Tuple_val<_This> _Myfirst; // 存儲(chǔ)的元素 }
原來,它有個(gè)成員叫_Myfirst,它就是用來存儲(chǔ)_This類型的變量的。你會(huì)看到_Myfirst的類型不是_This而是_Tuple_val<_This>,其實(shí),_Tuple_val又是一個(gè)類模板,它的代碼這里就不展開了,簡(jiǎn)而言之,它的作用是存儲(chǔ)一個(gè)tuple中的變量。_Myfirst._Val才是真正的元素。
這個(gè)tuple只存儲(chǔ)一個(gè)元素,類型為_Rest...的其他元素存在基類_MyBase即tuple<_Rest...>中。我們?nèi)匀灰詔uple<int, char, short>為例,tuple<int, char, short>存儲(chǔ)了一個(gè)int,有基類tuple<char, short>;而tuple<char, short>存儲(chǔ)了一個(gè)char,有基類tuple<short>;tuple<short>存儲(chǔ)了一個(gè)short,有基類tuple<>;tuple沒有基類也不存儲(chǔ)任何元素。
3.3 構(gòu)造函數(shù)
tuple的構(gòu)造函數(shù)沒什么可說的,就是初始化_Myfirst和_MyBase,當(dāng)然,_MyBase也要進(jìn)行么一個(gè)過程,直到tuple<>。
constexpr tuple(): _Mybase(), _Myfirst() {} constexpr explicit tuple(const _This& _This_arg, const _Rest&... _Rest_arg) : _Mybase(_Rest_arg...), _Myfirst(_This_arg) {} tuple(const _Myt&) = default; tuple(_Myt&&) = default;
它還提供了默認(rèn)拷貝構(gòu)造函數(shù)和移動(dòng)構(gòu)造函數(shù)(移動(dòng)語義是C++11中新增的特性,請(qǐng)自行查閱資料)。其實(shí),它還有很多構(gòu)造函數(shù),寫起來挺熱鬧,無非就是用不同的方式為它賦初值,故省略。
3.4 部分成員函數(shù)
tuple重載了賦值符號(hào)(=),這樣,tuple之間是可以賦值的
template<class... _Other> _Myt& operator=(const tuple<_Other...>& _Right) { // assign by copying same size tuple _Myfirst._Val = _Right._Myfirst._Val; _Get_rest() = _Right._Get_rest(); return (*this); }
賦值符號(hào)返回左邊的引用,這種行為和C++的內(nèi)置類型是一致的。_Get_rest是tuple的成員函數(shù),作用是把除了_Myfirst之外的那些元素拿出來。
接下來是成員函數(shù)_Equals,
template<class... _Other> constexpr bool _Equals(const tuple<_Other...>& _Right) const { static_assert(_Mysize == sizeof...(_Other), "comparing tuple to object with different size"); return (_Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest())); }
其中進(jìn)行了靜態(tài)斷言,如果兩個(gè)tuple的元素個(gè)數(shù)不相同,會(huì)引發(fā)一個(gè)編譯時(shí)的錯(cuò)誤。如果對(duì)應(yīng)的類型不能用==進(jìn)行比較,在模板特化時(shí)也會(huì)引發(fā)編譯期的錯(cuò)誤,例如,tuple<std::string, int>不能和tuple<int, char>比較,因?yàn)閟td::string和int是不能用==進(jìn)行比較的。
前面提到的_Get_rest在這里:
_Mybase& _Get_rest() noexcept { return (*this); } constexpr const _Mybase& _Get_rest() const noexcept { return (*this); }
它返回對(duì)基類的引用。*this的類型雖然是_Myt,但根據(jù)C++語法(可以把派生類的引用賦給對(duì)基類的引用),所以這樣做是沒問題的。
以上就是C++標(biāo)準(zhǔn)庫元組(tuple)源碼淺析的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
C語言中關(guān)于庫函數(shù) qsort 快排的用法
快速排序Qsort是所有學(xué)習(xí)算法和數(shù)據(jù)結(jié)構(gòu)最基礎(chǔ)的一個(gè)部分,也是考試題和面試的一個(gè)小重點(diǎn)。本片文章帶你了解Qsort的詳細(xì)用法規(guī)則2021-09-09C++11/14 線程中使用Lambda函數(shù)的方法
這篇文章主要介紹了C++11/14 線程中使用Lambda函數(shù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01詳解C語言隨機(jī)數(shù)設(shè)置的三種方式(保姆級(jí)教程)
本篇文章將為大家介紹在C語言中設(shè)置隨機(jī)數(shù)的三大方法的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定的幫助,需要的可以參考一下2022-11-11C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C++實(shí)現(xiàn)重載矩陣的部分運(yùn)算符
這篇文章主要為大家詳細(xì)介紹了如何利用C++實(shí)現(xiàn)重載矩陣的部分運(yùn)算符,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定幫助,需要的可以參考一下2022-10-10