用 C++ 模板元編程實現有限的靜態 introspection


C++ 中的奇技淫巧大部分來源於模板技術,尤其是模版元編程技術(Template Meta-Programming, TMP)。TMP 通過將一部分計算任務放在編譯時完成,不僅提高了程序的性能,還能讓程序獲得一些用常見語法結構無法實現的功能。在這里,我總結了幾個利用 TMP 實現靜態反射的例子,這些功能得益於模板的特化或模板實例化時的 SFINAE 行為。(代碼默認包含 <iostream> 頭)

1. 類型判定

#define MakeIsType(Tp) \
template <typename T> \
class Is_##Tp { \
public: \
    enum {value = false}; \
}; \
template <> \
class Is_##Tp<Tp> { \
public: \
    enum {value = true}; \
};

// 生成 Is_void 類
MakeIsType(void);

int main(int argc, char const *argv[])
{
    std::cout << Is_void<int>::value;    // 0
        std::cout << Is_void<void>::value;  // 1
    return 0;
}

以上代碼用了簡單的特化,先定義一個 value 為 false 的基礎類,並為 Tp 類特化一個 value 為 true 的模板。

2. 判定指針是否能轉換

template <typename To, typename From>
class IsConvertable {
    typedef char One;
    typedef struct { One _[2]; } Two;

    static One deduce(To*);
    static Two deduce(...);
public:
    enum { value = sizeof(deduce((From*)0)) == sizeof(One) };
};

int main() {
    std::cout << IsConvertable<long int, long>::value; // 1
    std::cout << IsConvertable<long int, double>::value; // 0
}

這里利用了成員函數的重載解析,如果 From 指針無法轉換為 To 指針(無論是同類,還是子類轉基類),那么第二個版本的 deduce 將被 sizeof 解析,完成判定的功能。( sizeof 並不會運行函數,但是會讓編譯器進行重載解析)

3. 成員名稱檢測

template <typename T>
class Has_foo {
    typedef char One;
    typedef struct { One _[2]; } Two;
    struct Base {
        char foo;
    };
    struct Mixin : public T, public Base {};
    template <typename U, U>
    struct Matcher {};
    template <typename U>
    static One deduce(U*, Matcher<char Base::* ,&U::foo>* = 0);
    static Two deduce(...);
public:
    enum { value = sizeof(deduce((Mixin*)0)) == sizeof(Two) };
};

class A
{
public:
    int foo(int, double);
};

class B
{
public:
    int bar(int, double);
};

int main(int argc, char const *argv[]) {
    std::cout << Has_foo<A>::value; // 1
    std::cout << Has_foo<B>::value; // 0
    return 0;
}

這里用到了 SFINAE 技術,如果 A 含有 foo 成員,那么 Mixin 中就會有兩個版本的 foo,一個來自 Base,一個來自 A,因為 Mixin 是一個多重繼承的子類,調用 &U::foo 在 U 為 Mixin 時將會產生二義性,所以這個版本的 deduce 將不會產生。sizeof將解析到 Two 這個版本。

4. 成員函數(包含參數和返回值類型)檢測

template<typename T, typename RESULT, typename ARG1, typename ARG2>
class HasMethod_foo
{
    template <typename U, RESULT (U::*)(ARG1, ARG2)> struct Matcher;
    template <typename U> static char deduce(Matcher<U, &U::foo> *);
    template <typename U> static int deduce(...);
  public:
    enum { value = sizeof(deduce<T>(0)) == sizeof(char) };
};

class A
{
public:
    int foo(int, double);
};

int main(int argc, char const *argv[]) {
    std::cout << HasMethod_foo<A, int, int, int>::value; // 0
    std::cout << HasMethod_foo<A, int, int, double>::value; // 1
    return 0;
}

這里采用了 SFINAE 技術,並利用了 C++ 中模板的非類型參數可以是成員指針這一性質,用一個 Matcher 類將 U 和某一類型的成員函數指針綁定在一起,如果一個類不存在這樣的成員函數,那么第一個版本的 deduce 將無法生成。於是 sizeof 將解析到第二個版本。

結論

關於更多模板元編程的技術,可以參考 C++ Template 這本書。stackoverflow 也是一個獲取這類奇技淫巧的好地方。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM