C++11 模板元編程 - Traits in TLP


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):用于判斷類(lèi)型T是否可以默認(rèn)轉(zhuǎn)型為U類(lèi)型;

  • __is_both_convertible(T, U):用于判斷類(lèi)型T和U之間是否可以互相轉(zhuǎn)型;

  • __is_base_of(T, U):用于判斷類(lèi)型T是否是類(lèi)型U的父類(lèi);

  • __is_built_in(T):用于判斷類(lèi)型T是否為C++內(nèi)置類(lèi)型;

  • __lambda_return(Lambda):針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,計(jì)算其返回值類(lèi)型;

  • __lambda_paras(Lambda):針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,計(jì)算其參數(shù)類(lèi)型;將所有參數(shù)類(lèi)型放在一個(gè)TypeList中返回;

  • __lambda_para(Lambda,Index):針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,返回其Index位置的參數(shù)的類(lèi)型。如果沒(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)常把變化的算法交給客戶(hù)自定義,然后通過(guò)語(yǔ)言提供的技術(shù)手段再注入給框架。相比傳統(tǒng)使用虛接口做注入,支持lambda會(huì)讓客戶(hù)的代碼更加簡(jiǎn)潔和緊湊。在框架中,我們可能會(huì)要提取用戶(hù)注入的lambda表達(dá)式的返回值類(lèi)型或者參數(shù)類(lèi)型。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)萃取出我們想要的類(lèi)型信息的:template<typename C, typename R, typename ... Args> struct LambdaTraits<R (C::*)(Args...) const>。我們把返回值類(lèi)型保存在ReturnType,而把所有的參數(shù)類(lèi)型保存在一個(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)行了萃取,判斷其返回類(lèi)型是int,參數(shù)類(lèi)型分別是const int *char

上面的測(cè)試中,之所以要定義testLambdaTraits函數(shù),主要是為了從lambda表達(dá)式對(duì)象中提取出它的類(lèi)型,然后再傳給__lambda_return()等。這種通過(guò)函數(shù)模板來(lái)對(duì)具體對(duì)象或變量進(jìn)行推導(dǎo)以獲取其類(lèi)型的手段,在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)門(mén)EST本質(zhì)上是一個(gè)類(lèi),而auto f不能是定義為類(lèi)的成員的,但是可以定義到函數(shù)里面。

關(guān)于TLP中的trait就介紹到這里,更多的關(guān)于trait的應(yīng)用在后面的還會(huì)專(zhuān)門(mén)介紹。


Test in TLP

返回 C++11模板元編程 - 目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容