C++类模板的重载

0. 说在前面的话

由于在看QT5.15.2中的connect方法中的typedef QtPrivate::FunctionPointer<Func1> 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);
        }
    };

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

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

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

1. 验证

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

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

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

    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;
    };
    

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

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

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

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

2. 额外的几个验证

根据typedef List<Args...> 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. 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;
    

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

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 <typename A, typename... Tail> struct List2<A,Tail...> { int abtail; };
    
  2. 主模板不包含可变参数类型
    // 主模板不包含可变参数类型时,次模板可以包含可变参数类型
    template <typename A> struct Test { int primary; };
    template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; };
    /*
    * 将可变模板参数改掉后
    *   1. 若 template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; }; 存在则匹配此模板
    *   2. 若其不存在则匹配到主模板
    */ 
    //template <class A, typename B> struct Test<A(*) (B)> { int testAB; };
    /*
    * 以下两种写法都会被认为类模板参数太多,第一种是可能是因为主模板不包含可变参数,若次模板包含可变参数时
    * 可变参数需要放到可以被解释的参数中,如函数的形参
    * 
    * 第二种暂不清楚
    */
    //template <class A, typename... B> struct Test<A,B...> { int testAmultiB; };
    //template <class A, typename B> struct Test<A, B> { int testAB; };
    

3. 与特化的区分

特化写法如下:

template <typename A, typename B> struct templateSpecialization {};
template <typename A> struct templateSpecialization<A, int> {};
template <> struct templateSpecialization<char, int> {};

4. 测试的整体代码

// templateStruct.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
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 <typename A, typename... Tail> struct List2<A,Tail...> { int abtail; };


// 主模板不包含可变参数类型时,次模板可以包含可变参数类型
template <typename A> struct Test { int primary; };
template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; };
/*
* 将可变模板参数改掉后
*   1. 若 template <class A, typename... B> struct Test<A(*) (B...)> { int testAmultiB; }; 存在则匹配此模板
*   2. 若其不存在则匹配到主模板
*/ 
//template <class A, typename B> struct Test<A(*) (B)> { int testAB; };
/*
* 以下两种写法都会被认为类模板参数太多,第一种是可能是因为主模板不包含可变参数,若次模板包含可变参数时
* 可变参数需要放到可以被解释的参数中,如函数的形参
* 
* 第二种暂不清楚
*/
//template <class A, typename... B> struct Test<A,B...> { int testAmultiB; };
//template <class A, typename B> struct Test<A, B> { 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);

}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值