• C++类模板的重载


    0. 说在前面的话

    由于在看QT5.15.2中的connect方法中的typedef QtPrivate::FunctionPointer SignalType;使用以及,QtPrivate::FunctionPointer的声明。而产生的疑问,因此有了这篇内容。QtPrivate::FunctionPointer的声明如下:

    // 第一个
    template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; };
    // 第二个
    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
        {
            typedef Obj Object;
            typedef List<Args...>  Arguments;
            typedef Ret ReturnType;
            typedef Ret (Obj::*Function) (Args...);
            enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true};
            template <typename SignalArgs, typename R>
            static void call(Function f, Obj *o, void **arg) {
                FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
            }
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    QtPrivate::FunctionPointer 的声明总共有七个,这里不再一一粘贴,看到这里不知诸位会不会产生以下几个疑问:

    1. 第二个QtPrivate::FunctionPointer 后面尖括号中的内容是什么?
    2. 第二个QtPrivate::FunctionPointer 与第一个有什么关系?
    3. typedef QtPrivate::FunctionPointer SignalType;会最终匹配到哪个类型?

    我这里使用的环境是VS2019编写的代码, C++ 14的编译标准

    1. 验证

    1. 声明一个结构体模板,声明形式如下

      template <typename A> struct Test<A> {};
      
      • 1

      这种写法会产生如下错误
      在这里插入图片描述
      从这个错误提示中我们可以得到信息如下:

      1. 主模板(那有主模板是不是就由次模板呢?这里的次模板是我自己定义的,因为在网上没有查找到相关资料,若有知道官方资料地址,请评论区留言感谢!!!)
      2. 模板参数列表(模板参数列表起到的具体作用是什么?)
    2. 主模板与次模板一起声明
      次模板声明的官方标准暂未找到~~~
      这里的声明仿照了QT的源码

      template<class Obj> struct FunctionPointer {};
      template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret(Obj::*) (Args...)>
      {
          typedef Obj Object;
          enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true };
          typedef List<Args...>  Arguments;
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      接下来看FunctionPointer的用已经VS的智能提示
      在这里插入图片描述
      在这里插入图片描述
      看到这个智能提示诸位是否觉得有点像重载的提示呢?(这也是为什么文章标题是类模板的重载)。
      看到第一个提示大家应该很熟悉,就是任意类型。
      那第二个是什么呢?整体看起来有么有感觉它很想函数指针类型呢? 没错它就是一个函数指针类型,但确实一个类的成员函数指针类型,而(Args...)则表示该函数的参数。
      用其声明一个变量如下:

      /*
      template struct FunctionPointer
      声明的变量与结构体模板声明中各个类型的对象关系:
      	1. Obj:会被推导为MyClass
      	2. Ret:会被推导为void
      	3. Args:则会被推导为0个类型
      
      这里回答 "模板参数列表起到的具体作用是什么?" 这个问题,模板参数列表起到作用是指定这个模板类型接受的参数类型
      */ 
      FunctionPointer<void(MyClass::*)()> fpTest;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    到这里我想最初的那三个问题,小伙伴们应该已经知道答案了吧。问题如下:

    1. 第二个QtPrivate::FunctionPointer 后面尖括号中的内容是什么
    2. 第二个QtPrivate::FunctionPointer 与第一个有什么关系,我们都知道
    3. typedef QtPrivate::FunctionPointer SignalType;会最终匹配到那个类型

    2. 额外的几个验证

    根据typedef List Arguments;衍生的验证,List模板类型的声明如下:

    template <typename...> struct List { int primary; };
    template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; int ts; };
    
    • 1
    • 2
    1. List的使用及智能提示
      在这里插入图片描述
      在这里插入图片描述
      在第二个的智能提示中多个,且前面是空的,这个会有什么影响呢?看下面List的使用:

      // 可以看到,由于设置了两个类型,会被匹配到List的次模板(重载模板)
      List<int, string> list1;
      list1.ts = 0;
      
      // 一个类型同样会被匹配到次模板
      List<int> list2;
      list2.ts = 0;
      
      // 不设置类型的时候,被匹配到了主模板
      List<> list3;
      list3.primary = 0;
      
      // 多于两个类型的同样会被匹配到次模板
      List<int, string,int> list4;
      list4.ts = 0;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      目前看下来智能提示中多个,且前面是空的,好像对传入类型什么的并没有影响,这个应该是个标记(个人猜测),这个,目前测试看仅在主模板中存在可变模板参数时才会有。

    2.1 其他的几种写法

    1. 主模板包含可变参数类型
      // 主模板包含可变参数类型时,次模板才可以调整模板参数类型的个数,但次模板不可与主模板参数类型一致
      
      template <typename A, typename...> struct List2 { int primary; };
      template <typename A, typename B,typename... Tail> struct List2<A, B,Tail...> { int abtail; };
      template <typename A> struct List2<A> { int A; };
      template <typename A, typename B> struct List2<A,B> { int AB; };
      template <typename A, typename B, typename C> struct List2<A,B,C> { int ABC; };
      // 次模板与主模板参数类型一致 error
      // template  struct List2 { int abtail; };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. 主模板不包含可变参数类型
      // 主模板不包含可变参数类型时,次模板可以包含可变参数类型
      template <typename A> struct Test { int primary; };
      template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; };
      /*
      * 将可变模板参数改掉后
      *   1. 若 template  struct Test { int testAmultiB; }; 存在则匹配此模板
      *   2. 若其不存在则匹配到主模板
      */ 
      //template  struct Test { int testAB; };
      /*
      * 以下两种写法都会被认为类模板参数太多,第一种是可能是因为主模板不包含可变参数,若次模板包含可变参数时
      * 可变参数需要放到可以被解释的参数中,如函数的形参
      * 
      * 第二种暂不清楚
      */
      //template  struct Test { int testAmultiB; };
      //template  struct Test { int testAB; };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

    3. 与特化的区分

    特化写法如下:

    template <typename A, typename B> struct templateSpecialization {};
    template <typename A> struct templateSpecialization<A, int> {};
    template <> struct templateSpecialization<char, int> {};
    
    • 1
    • 2
    • 3

    4. 测试的整体代码

    // templateStruct.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include 
    using namespace std;
    class MyClass
    {
    public:
        MyClass() = default;
        ~MyClass() = default;
        void test(int,int) { return; }
    };
    
    
    template <typename A, typename B> struct templateSpecialization {};
    template <typename A> struct templateSpecialization<A, int> {};
    template <> struct templateSpecialization<char, int> {};
    
    
    template <typename...> struct List { int primary; };
    template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; int ts; };
    
    
    // 主模板包含可变参数类型时,次模板才可以调整模板参数类型的个数,但次模板不可与主模板参数类型一致
    
    template <typename A, typename...> struct List2 { int primary; };
    template <typename A, typename B,typename... Tail> struct List2<A, B,Tail...> { int abtail; };
    template <typename A> struct List2<A> { int A; };
    template <typename A, typename B> struct List2<A,B> { int AB; };
    template <typename A, typename B, typename C> struct List2<A,B,C> { int ABC; };
    // 次模板与主模板参数类型一致 error
    // template  struct List2 { int abtail; };
    
    
    // 主模板不包含可变参数类型时,次模板可以包含可变参数类型
    template <typename A> struct Test { int primary; };
    template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; };
    /*
    * 将可变模板参数改掉后
    *   1. 若 template  struct Test { int testAmultiB; }; 存在则匹配此模板
    *   2. 若其不存在则匹配到主模板
    */ 
    //template  struct Test { int testAB; };
    /*
    * 以下两种写法都会被认为类模板参数太多,第一种是可能是因为主模板不包含可变参数,若次模板包含可变参数时
    * 可变参数需要放到可以被解释的参数中,如函数的形参
    * 
    * 第二种暂不清楚
    */
    //template  struct Test { int testAmultiB; };
    //template  struct Test { int testAB; };
    
    
    
    
    
    
    
    template<class Obj> struct FunctionPointer {};
    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret(Obj::*) (Args...)>
    {
        typedef Obj Object;
        enum { ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true };
        typedef List<Args...>  Arguments;
    };
    
    template <typename Func1, typename Func2>
    static inline void connect(const typename FunctionPointer<Func1>::Object* sender, Func1 signal,
        const typename FunctionPointer<Func2>::Object* receiver, Func2 slot)
    {
        const type_info& nInfo = typeid(Func1);
        cout << nInfo.name() << " | " << nInfo.raw_name() << " | " << nInfo.hash_code() << endl;
        const type_info& inInfo = typeid(FunctionPointer<Func1>::Arguments::Car);
        cout << inInfo.name() << " | " << inInfo.raw_name() << " | " << inInfo.hash_code() << endl;
    
        cout << FunctionPointer<Func1>::ArgumentCount << endl;
    
    }
    
    int main()
    {
        typedef void (*ptr)();
        Test<ptr> test1;
        test1.testAmultiB;
        
        FunctionPointer<void(MyClass::*)()> fpTest;
    
        List<int,string> list1;
        list1.ts = 0;
    
        List<int> list2;
        list2.ts = 0;
    
        List<> list3;
        list3.primary = 0;
    
        List<int, string,int> list4;
        list4.ts = 0;
    
    
        List2<int> list21;
        list21.A = 0;
        
        List2<int,int> list22;
        list22.AB = 0;
    
        List2<int,int,int> list23;
        list23.ABC;
    
    
        MyClass a;
        connect(&a, &MyClass::test, &a, &MyClass::test);
    
    }
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
  • 相关阅读:
    mysql-修改数据存储位置
    Java冒泡排序
    钢材缺陷检测系统-ui界面
    Java_移位运算简述
    莫慌!Java 多商户外贸版系统这不就来了么
    win10 配置 oh-my-posh
    完美的错误处理:Go 语言最佳实践分享
    自动出价下机制设计系列 (二) : 面向私有约束的激励兼容机制设计
    【个人总结】动态路由实现方案
    怎么从C语言过渡到C++
  • 原文地址:https://blog.csdn.net/weixin_41111116/article/details/127821651