C++中的几种可调用对象
1.函数
2.函数指针
3.lambda表达式
4.bind创建的对象
5.重载了函数调用运算符()的类
和其他对象一样,可调用的对象也有类型,每个lambda有它自己唯一的(未命名)类类型,函数及函数指针的类型则由其返回值类型和实参类型决定,然而俩个不同的类型的可调用对象却可能共享同一种调用形式,调用形式指明了返回的类型以及传递给调用的实参类型。不同类型可能具有相同的调用形式,对于几个可调用对象共享同一个调用形式的情况,有时我们会希望把它们看成具有相同的类型。
上面这些可调用对象分别对其参数执行了不同的算术运算,尽管它们的类型各不相同,但是共享同一种调用形式: int ( int , int )
我们可能希望使用这些可调用对象构建一个简单的桌面计算器,为了实现这一目的,需要定义一个函数表,用来存储指向这些可调用对象的"指针",当程序需要执行某个特定的操作时,从表中查找该调用的函数,这个表可以通过map实现,关键字为string,值为对应函数,则可以这样定义
map<string,int(*)(int,int)> binops;
这样我们可以将add的指针添加到binops中,但是我们不能将mod或者divide存入binops,问题在于mod是个lambda表达式,每个lambda有它自己的类类型,该类型与存储在binops中的值的类型不匹配。(引用至C++primer,但个人实践时是可以进行insert的)
binops.insert({"+",add}); //{"+",add}是一个pair
binops.insert({"%",mod}); //错误mod不是一个函数指针
标准库function类型
可以用function来定义同一调用形式的可调用对象的类型,这样就可以解决上面的问题。
function是一个模板,和我们使用过的其他模板一样,当创建一个具体的function类型时我们必须提供额外的信息,这里的额外信息指该function类型能够表示的对象的调用形式,因此,我们可以用这个新声明的类型表示任意一种桌面计算器用到的类型。
我们能把所有可调用对象,包括函数指针、lambda或者函数对象在内,都添加到这个map中
重载的函数与function
我们不能(直接)将重载函数的名字存入function类型的对象中,这样会产生二义性
int add(int i, int j)
{
return i + j;
}
double add(double i, double j)
{
return i + j;
}
int main()
{
map<string, function<int(int, int)>> binops;
binops.insert({ "+",add }); //错误,无法确认添加哪个add
}
解决上述二义性问题的一条途径是存储函数指针,也可以使用lambda来消除二义性。