zoukankan      html  css  js  c++  java
  • 用 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 也是一个获取这类奇技淫巧的好地方。

  • 相关阅读:
    JavaScript 操作 Cookie
    Java监控文件夹变化
    Cookie与Session的区别
    常用插件
    Plugin 'org.springframework.boot:springbootmavenplugin:' not found
    mysql安装(windows)
    idea 安装社区版
    linux安装tomcat
    将克隆的项目上传到自己的github
    tomcat安装配置
  • 原文地址:https://www.cnblogs.com/heleifz/p/cpp-template-reflection.html
Copyright © 2011-2022 走看看