tuple實現原理
C++從11標準開始就引入了一個tuple元組,這種類型很方便的存儲各種不同類型的元素;我們知道在C++中,都需要一個唯一確定的類型才能定義一個變量和分配內存結構,那麼tuple具體是怎麼實現的呢?
1. 可變參數的模板
我們來看一個代碼:
void printx()
{
}
template<typename Type, typename... Types>
void printx(const Type& arg1, const Types&... args)
{
std::cout << arg1 << std::endl;
std::cout << "args: " << sizeof...(args) << std::endl;
printx(args...);
}
int main(int args, char* argv[])
{
printx(100, "hello", "world", 3.14, 200);
return 0;
}
這個程序的輸出爲:
100
args: 4
hello
args: 3
world
args: 2
3.14
args: 1
200
args: 0
如果不瞭解C++11 模板特性的童鞋,對於這個代碼可能有點不理解,這裏做一個解釋。
template<typename Type, typename... Types>
: 這一行中typename... Types
定義一個可變類型個數的模板聲明,並且Types被稱爲模板參數包,在這個聲明中:- 模板參數個數可變。
- 模板參數的類型可以不同。
- 模板參數的個數可以是0個。
void printx(const Type& arg1, const Types&... args)
: 在這一行中const Types&... args
定義可變類型和個數的參數,args被稱作爲函數參數包,在這個聲明中:- 模板參數個數可變。
- 模板參數的類型可以不同。
- 模板參數的個數可以是0個。
sizeof...(args)
: 這一行中計算參數包中參數的個數。printx(args...);
: 這一行中args...
代表整個參數包。
所以從上面的調用可以發現,整個調用過程形成一個遞歸調用,每次調用導致參數包的個數減少一個,並且我們定義了一個void printx()
函數爲退出函數(參數包的個數爲0, 代表空參數調用);
2. MyTuple的定義
根據上面模板參數包的轉發,我們可以很容易的定義出一個可變參數的類的模板參數的轉發定義,如下:
template<typename...Values>
class MyTuple;
template<>
class MyTuple<>
{
};
template<typename Head, typename... Tail>
class MyTuple<Head, Tail...> : public MyTuple<Tail...>
{
public:
Head m_Data;
};
int main(int args, char* argv[])
{
MyTuple<int, double, char, short> t;
return 0;
}
整個定義的t結構體如下:
那麼這個類是怎麼定義的呢?整體也可以定義成爲遞歸狀態,如下:
- 第一次類類型爲
MyTuple<int, double, char, short>
, 此時繼承的是MyTuple<double, char, short>
. - 第二次類類型爲
MyTuple<double, char, short>
, 此時繼承的是MyTuple<char, short>
. - 第三次類類型爲
MyTuple<char, short>
, 此時繼承的是MyTuple<short>
。 - 第四次類型爲
MyTuple<short>
, 此時繼承的是MyTuple<>
. - 第五次爲終止類型爲
MyTuple<>
。
對於std::tuple
的實現原理其實跟這個也是一樣的,定義如下:
template<class _This,
class... _Rest>
class tuple<_This, _Rest...>
: private tuple<_Rest...>
{ // recursive tuple definition
_Tuple_val<_This> _Myfirst; // the stored element
};