欧美大屁股bbbbxxxx,狼人大香伊蕉国产www亚洲,男ji大巴进入女人的视频小说,男人把ji大巴放进女人免费视频,免费情侣作爱视频

歡迎來到入門教程網(wǎng)!

C語言

當(dāng)前位置:主頁 > 軟件編程 > C語言 >

淺析C++標(biāo)準(zhǔn)庫元組(tuple)源碼

來源:本站原創(chuàng)|時間:2020-01-10|欄目:C語言|點擊: 次

一、什么是元組

元組不是什么新鮮東西,在數(shù)學(xué)、python語言還有我們今天要說的C++都有元組。

簡單地說,元組就是一組東西,例如,在講代數(shù)拓?fù)涞臅r候,經(jīng)常把拓?fù)淇臻gX和其中一點x作為一個偶對(X, x),這其實就是個元組,點的坐標(biāo)也可以看成一個元組。C++中的元組(tuple)是這個樣子的:

std::tuple<int, std::string> tu{ 2,"12iop" };

一個tuple可以包含不同類型的成員,例如上面的tu包含一個int和一個字符串。

二、用法

在考察源碼之前,我們必須先知道它的用法。

要想使用tuple,要包含頭文件<tuple>:

#include <tuple>

tuple實際上是一個有可變參數(shù)的類模板,使用的時候,傳入若干個參數(shù)將其特化。

struct Point
{
 int x;
 int y;
};
 
void main()
{
 std::tuple<int, std::string> t1{ 1,"qwer" }; // 一個由int和字符串組成的tuple
 constexpr std::tuple<int, void*> t2{ 1,nullptr }; // 一個由int和void*組成的tuple
 std::tuple<int, Point> t3{ 1,{20,89} }; // 一個由int和Point結(jié)構(gòu)體組成的tuple
 std::tuple<int, char, std::string> t4{ 1,'t',"qwer" }; // 一個由int、char、字符串組成的tuple
}

上面的代碼中,我用constexpr修飾了t2,這是完全正確的,std::tuple的構(gòu)造函數(shù)是constexpr的。

獲取tuple中的值,用std::get。這不是函數(shù),而是函數(shù)模板,我們需要傳入size_t類型的變量將其特化,或者傳入一個類型,告訴它我們需要取出元組中的哪個類型的成員。

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也是一個編譯時的常量。

我們通過get<char>的方式得到了s,它是char類型的變量。std::get<T>可以從tuple中獲取到第一個類型為T的成員。

tuple也可以用【==】和【!=】比較是否相等:

std::tuple<int, std::string> t5{ 1,"qwer" };
 
if (t1 == t5)
{
 std::cout << "==" << std::endl;
}

介紹tuple的用法不是本文的主要內(nèi)容,故到此為止。有興趣的同學(xué)可以自行查閱資料。

接下來,是時候考察一看源碼了。

三、源碼分析

tuple是個可變參數(shù)的類模板:

template<typename... _Types>
class tuple;

這是對類模板的聲明。

接下來,實現(xiàn)參數(shù)個數(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是個空的結(jié)構(gòu)體,暫時不管它。_Myt就是tuple<>自己,這樣寫起來方便一些。

tuple<>定義了空的構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)(空tuple沒什么可做的)。

成員函數(shù)swap用于與另一個tuple<>交換內(nèi)容,因為沒什么可交換的,函數(shù)體當(dāng)然是空的。

_Equals用來判斷兩個tuple<>是否相等,它返回true,這是顯然的(所有的tuple<>都是一個樣子)。

_Less從函數(shù)名看,是為了比較大小,但如果遇到?jīng)]有重載<的類型呢?暫時不管它。

有了空tuple的定義,就可以定義非空的tuple。

3.2 非空的tuple

template<class _This,
class... _Rest>
class tuple<_This, _Rest...>
 : private tuple<_Rest...>
{
 // 內(nèi)容
}

n(>0)個元素的tuple私有繼承了n-1個元素的tuple。顯然這是一種遞歸定義,最終會遞歸到tuple<>,而tuple<>是已經(jīng)定義好了得。

例如,tuple<int, char, short>私有繼承了tuple<char, short>,而tuple<char, short>又私有繼承了tuple<short>,tuple<short>私有繼承了tuple<>。由于私有繼承可以實現(xiàn)“has-a”功能,所以,這樣的方式可以將不同類型的對象組合在一起。如下圖:

那么,tuple是如何存儲其中的元素呢?

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; // 存儲的元素
}

原來,它有個成員叫_Myfirst,它就是用來存儲_This類型的變量的。你會看到_Myfirst的類型不是_This而是_Tuple_val<_This>,其實,_Tuple_val又是一個類模板,它的代碼這里就不展開了,簡而言之,它的作用是存儲一個tuple中的變量。_Myfirst._Val才是真正的元素。

這個tuple只存儲一個元素,類型為_Rest...的其他元素存在基類_MyBase即tuple<_Rest...>中。我們?nèi)匀灰詔uple<int, char, short>為例,tuple<int, char, short>存儲了一個int,有基類tuple<char, short>;而tuple<char, short>存儲了一個char,有基類tuple<short>;tuple<short>存儲了一個short,有基類tuple<>;tuple沒有基類也不存儲任何元素。

3.3 構(gòu)造函數(shù)

tuple的構(gòu)造函數(shù)沒什么可說的,就是初始化_Myfirst和_MyBase,當(dāng)然,_MyBase也要進(jìn)行么一個過程,直到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ù)和移動構(gòu)造函數(shù)(移動語義是C++11中新增的特性,請自行查閱資料)。其實,它還有很多構(gòu)造函數(shù),寫起來挺熱鬧,無非就是用不同的方式為它賦初值,故省略。

3.4 部分成員函數(shù)

tuple重載了賦值符號(=),這樣,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);
 }

賦值符號返回左邊的引用,這種行為和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)斷言,如果兩個tuple的元素個數(shù)不相同,會引發(fā)一個編譯時的錯誤。如果對應(yīng)的類型不能用==進(jìn)行比較,在模板特化時也會引發(fā)編譯期的錯誤,例如,tuple<std::string, int>不能和tuple<int, char>比較,因為std::string和int是不能用==進(jìn)行比較的。

前面提到的_Get_rest在這里:

_Mybase& _Get_rest() noexcept
{
 return (*this);
}
 
constexpr const _Mybase& _Get_rest() const noexcept
{
 return (*this);
}

它返回對基類的引用。*this的類型雖然是_Myt,但根據(jù)C++語法(可以把派生類的引用賦給對基類的引用),所以這樣做是沒問題的。

以上就是C++標(biāo)準(zhǔn)庫元組(tuple)源碼淺析的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。

網(wǎng)頁制作CMS教程網(wǎng)絡(luò)編程軟件編程腳本語言數(shù)據(jù)庫服務(wù)器

如果侵犯了您的權(quán)利,請與我們聯(lián)系,我們將在24小時內(nèi)進(jìn)行處理、任何非本站因素導(dǎo)致的法律后果,本站均不負(fù)任何責(zé)任。

聯(lián)系QQ:835971066 | 郵箱:835971066#qq.com(#換成@)

Copyright © 2002-2020 腳本教程網(wǎng) 版權(quán)所有