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
的声明总共有七个,这里不再一一粘贴,看到这里不知诸位会不会产生以下几个疑问:
- 第二个
QtPrivate::FunctionPointer
后面尖括号中的内容是什么? - 第二个
QtPrivate::FunctionPointer
与第一个有什么关系? typedef QtPrivate::FunctionPointer<Func1> SignalType;
会最终匹配到哪个类型?
我这里使用的环境是VS2019
编写的代码, C++ 14
的编译标准
1. 验证
-
声明一个结构体模板,声明形式如下
template <typename A> struct Test<A> {};
这种写法会产生如下错误
从这个错误提示中我们可以得到信息如下:主模板
(那有主模板是不是就由次模板呢?这里的次模板是我自己定义的,因为在网上没有查找到相关资料,若有知道官方资料地址,请评论区留言感谢!!!)模板参数列表
(模板参数列表起到的具体作用是什么?)
-
主模板与次模板一起声明
次模板声明的官方标准暂未找到~~~
这里的声明仿照了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;
到这里我想最初的那三个问题,小伙伴们应该已经知道答案了吧。问题如下:
- 第二个
QtPrivate::FunctionPointer
后面尖括号中的内容是什么 - 第二个
QtPrivate::FunctionPointer
与第一个有什么关系,我们都知道 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; };
-
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 其他的几种写法
- 主模板包含可变参数类型
// 主模板包含可变参数类型时,次模板才可以调整模板参数类型的个数,但次模板不可与主模板参数类型一致 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; };
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);
}