C++標(biāo)準(zhǔn)庫(kù)STL中的type_traits
文件中喊式,已經(jīng)有了比較全面的C++ trait組件亮曹,可以用來(lái)對(duì)代碼做各種靜態(tài)反射。
TLP庫(kù)中補(bǔ)充了如下幾個(gè)有用的trait工具宅此,這些trait在后面介紹的TLP的sample代碼中會(huì)用到秦踪。
__is_convertible(T, U)
:用于判斷類型T是否可以默認(rèn)轉(zhuǎn)型為U類型褐捻;__is_both_convertible(T, U)
:用于判斷類型T和U之間是否可以互相轉(zhuǎn)型;__is_base_of(T, U)
:用于判斷類型T是否是類型U的父類椅邓;__is_built_in(T)
:用于判斷類型T是否為C++內(nèi)置類型柠逞;__lambda_return(Lambda)
:針對(duì)一個(gè)Lambda表達(dá)式類型,計(jì)算其返回值類型景馁;__lambda_paras(Lambda)
:針對(duì)一個(gè)Lambda表達(dá)式類型板壮,計(jì)算其參數(shù)類型;將所有參數(shù)類型放在一個(gè)TypeList中返回合住;__lambda_para(Lambda绰精,Index)
:針對(duì)一個(gè)Lambda表達(dá)式類型撒璧,返回其Index位置的參數(shù)的類型。如果沒(méi)有參數(shù)返回NullType笨使;
這些trait工具的實(shí)現(xiàn)大多在前面都已經(jīng)介紹過(guò)卿樱,除過(guò)最后三個(gè)關(guān)于lambda的。
C++11引入了lambda特性硫椰,這是一個(gè)非常有用的特性繁调,尤其對(duì)于編寫(xiě)框架。現(xiàn)實(shí)中我們經(jīng)常把變化的算法交給客戶自定義最爬,然后通過(guò)語(yǔ)言提供的技術(shù)手段再注入給框架涉馁。相比傳統(tǒng)使用虛接口做注入门岔,支持lambda會(huì)讓客戶的代碼更加簡(jiǎn)潔和緊湊爱致。在框架中,我們可能會(huì)要提取用戶注入的lambda表達(dá)式的返回值類型或者參數(shù)類型寒随。TLP中的__lambda_return()
糠悯、__lambda_paras()
和__lambda_para()
就是幫助代碼完成這些事情的。它們的實(shí)現(xiàn)如下:
// "tlp/traits/LambdaTraits.h"
template<typename T>
struct LambdaTraits
: LambdaTraits<decltype(&T::operator())>
{
};
template<typename C, typename R, typename ... Args>
struct LambdaTraits<R (C::*)(Args...) const>
{
using ReturnType = R;
using ParaTypes = typename TypeList<Args...>::Result;
};
#define __lambda_return(...) typename LambdaTraits<__VA_ARGS__>::ReturnType
#define __lambda_paras(...) typename LambdaTraits<__VA_ARGS__>::ParaTypes
#define __lambda_para(lambda, index) __at(__lambda_paras(lambda), __int(index))
如上妻往,我們主要靠模板特化的模式匹配特性來(lái)萃取出我們想要的類型信息的:template<typename C, typename R, typename ... Args> struct LambdaTraits<R (C::*)(Args...) const>
互艾。我們把返回值類型保存在ReturnType
,而把所有的參數(shù)類型保存在一個(gè)TypeList中:using ParaTypes = typename TypeList<Args...>::Result
讯泣。
可以如下測(cè)試這些lambda traits:
TEST("calculate the return type and parameter types of a lambda")
{
template<typename T>
void testLambdaTraits(const T&)
{
ASSERT_EQ(__lambda_return(T), int);
ASSERT_EQ(__lambda_paras(T), __type_list(const int*, char));
ASSERT_EQ(__lambda_para(T, 0), const int*);
ASSERT_EQ(__lambda_para(T, 1), char);
ASSERT_EQ(__lambda_para(T, 2), __null());
}
void run()
{
testLambdaTraits([](const int* x, char y){ return *x + y; });
}
}纫普;
上面我們對(duì)lambda表達(dá)式[](const int* x, char y){ return *x + y; }
進(jìn)行了萃取,判斷其返回類型是int
好渠,參數(shù)類型分別是const int *
和char
昨稼。
上面的測(cè)試中,之所以要定義testLambdaTraits
函數(shù)拳锚,主要是為了從lambda表達(dá)式對(duì)象中提取出它的類型假栓,然后再傳給__lambda_return()
等。這種通過(guò)函數(shù)模板來(lái)對(duì)具體對(duì)象或變量進(jìn)行推導(dǎo)以獲取其類型的手段霍掺,在C++11之前是一種常用技巧匾荆。由于C++11引入了decltype
關(guān)鍵字,所以以后都不用再這么繞了杆烁!我們重新實(shí)現(xiàn)測(cè)試用例如下:
TEST("calculate the return type and parameter types of a lambda")
{
void run()
{
auto f = [](const int* x, char y){ return *x + y; };
using Lambda = decltype(f);
ASSERT_EQ(__lambda_return(Lambda), int);
ASSERT_EQ(__lambda_paras(Lambda), __type_list(const int*, char));
ASSERT_EQ(__lambda_para(Lambda, 0), const int*);
ASSERT_EQ(__lambda_para(Lambda, 1), char);
ASSERT_EQ(__lambda_para(Lambda, 2), __null());
}
};
之所以還需要run()
方法,是因?yàn)門EST本質(zhì)上是一個(gè)類兔魂,而auto f
不能是定義為類的成員的入热,但是可以定義到函數(shù)里面晓铆。
關(guān)于TLP中的trait就介紹到這里绰播,更多的關(guān)于trait的應(yīng)用在后面的還會(huì)專門介紹蠢箩。