函数容器

​ 以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出错