函数容器
以vector容器为例,制作一个储存函数的容器:
由于VC++编译器对typeid().name()的支持比较人性化,本文选用VC++2017为编译器。
普通函数容器
普通函数,即:非类成员函数。
函数的类型
实现先看看函数的类型:
void f(){}
cout << typeid(f).name() << endl
<< typeid(void()).name() << endl
<< typeid(int()).name() << endl
<< typeid(int(int)).name() << endl;
输出结果如下:
void __cdecl(void)
void __cdecl(void)
int __cdecl(void)
int __cdecl(int)
性质
#include <type_traits>
std::is_move_assignable<void(void)>::value; // void(void)是否可移动、可转让?----否
函数指针类型:
void f(){}
cout << typeid(&f).name()<< endl;
输出结果如下:
void (__cdecl*)(void)
性质
#include <type_traits>
std::is_move_assignable<void(*)(void)>::value; // void(void)是否可移动、可转让?----是
函数类型容器
首先尝试声明函数类型容器:
vector<void()>lst_fic;
lst_fic.push_back(f);
编写过程无误,但是VC++和GCC8下编译失败。
GCC8提示如下:
required from ‘class std::allocator<void()>’
很明显,要求提供void()类型的分配器,但是标准库中并未提供。说明无法声明函数类型容器。
函数类型数组
decltype(int()) arr_fic[5]; // 这个是int数组,而不是int(void)
decltype(f) arr_fic[5];
在编译第二条语句时报错:
“arr_fic”数组元素类型不能是函数或抽象类类型
函数指针类型数组
void f() {
cout << "f()" << endl;
}
void f1() {}
using type_f=void (*)(); // 本来想用typename 但是发现没什么用
type_f p_f_array[2]{// 此处可写作:decltype(f) p_f_array[2];
f, f1// 该容器只能容纳void(void)类型
};
p_f_array[0]();
cout << typeid(p_f_array).name() << endl
<< typeid(p_f_array[0]).name();
输出结果如下:
f()
void (__cdecl*[2])(void)
void (__cdecl*)(void)
函数指针容器
vector<void(*)(void)> vec_fic{
f, f1
};
vec_fic.at(0)();
cout << typeid(vec_fic).name() << endl
<< typeid(vec_fic[0]).name() << endl;
结果如下:
f()
class std::vector<void (__cdecl*)(void),class std::allocator<void (__cdecl*)(void)> >
void (__cdecl*)(void)
类函数容器
类成员函数的行为与普通函数的行为不同,由于非静态成员函数的调用必须要先声明对象。class::f被认为是调用类成员函数,导致必须先声明一个对象。获取类成员函数地址的方式是进行取地址:&class::f。
以此类为例:
class A{
public:
void f(){
cout<<"f()"<<endl;
}
void f1(){}
};
1. 成员函数类型
cout<< typeid(&A::f).name();
输出结果为:
void (__cdecl A::*)(void) __ptr64
可以看到,比普通函数后面多了一个__ptr64,成员函数指针比普通函数指针大一倍。
2.类成员函数指针
声明方式与普通函数相同,但是加入了限定符:
void (A::*f)()=&A::f;
cout << typeid(&A::f).name() << endl
<< typeid(f).name();
输出结果为:
void (__cdecl A::*)(void) __ptr64
void (__cdecl A::*)(void) __ptr64
3. 成员函数数组
using member_ptr=void (A::*)();
member_ptr arr_mbr[2]{
&A::f, &A::f1
};
A a;
(a.*arr_mbr[0])();
cout << typeid(arr_mbr).name() << endl
<< typeid(arr_mbr[0]).name();
输出结果如下:
f()
void (__cdecl A::*[2])(void) __ptr64
void (__cdecl A::*)(void) __ptr64
可以看到,A::f()被成功调用。
4. 成员函数容器
vector<void (A::*)()> vec_mbr{
&A::f, &A::f1
};
A a;
(a.*vec_mbr[0])();
cout << typeid(vec_mbr).name() << endl
<< typeid(vec_mbr[0]).name();
输出结果如下:
f()
class std::vector<void (__cdecl A::*)(void) __ptr64,class std::allocator<void (__cdecl A::*)(void) __ptr64> >
void (__cdecl A::*)(void) __ptr64
总结
虽然函数名就代表了函数地址,但是函数名的类型和函数指针的类型不同。而且由于函数的大小不固定,因此无法创建函数数组,但是函数指针的大小是固定的,因此可以创建函数指针的数组和容器。
decltype()与typeid的不一致
typeid将int()识别为int --cdecl(void)
而decltype识别为int
decltype(f) a;
p_f_array[0]();
cout << typeid(f2).name() << endl
<< typeid(a).name();
// a(); 该行无法link出错
。