问题描述
C++ 中的可调用对象与其他编程语言中的函数对象有何异同?
问题解答
回答1:题目问的其它语言,这个太广泛了,因为编程语言这么多,有形如C语言这种传统的函数,也有形如java那样无法独立存在的函数,还有各类脚本语言的不同形式的函数。
不过在函数是第一等公民的语言里,要求函数能做到
函数可以独立存在,且可在任意地方执行
函数的作用域是词法作用域,且可捕获外部变量
函数可从另外一个函数返回,也可以作为别的函数的参数(高阶函数)
函数从别的函数生成后,所引用的作用域依旧可访问,而不是被销毁
在不少脚本语言和新兴语言里,函数大都是有以上特性表现的。
关于不同语言闭包捕获的问题,可以参考一下我之前写过的一篇文章 https://zhuanlan.zhihu.com/p/...
再说C++,C++有四个可以用来调用的东西
函数
函数指针
重载了()运算符的类
lambda
这四个东西的类型各不相同,但是都可以做到执行函数,使用上都是形如func()。
函数和函数指针在一定情况下是可以互转的,它在C语言里就存在,但是它存在一些难以解决的问题,包括
函数类型难写,难读
函数内部无法保存状态,没有办法实现外层变量捕获
重载了()运算符的类是C++提供的一个仿函数写法,它可以实现不少做法
class Test{public: Test(int a):_a(a){} int operator ()(int b){ return _a+b; } private: int _a;};
在使用上只要这样:
Test sum1 = Test(1);std::cout << sum1(10) << ’n’;Test sum2 = Test(10);std::cout << sum2(10) << ’n’;
可以看到sum1和sum2的内部都保存了一个状态,进而表现不一,这是普通的函数和函数指针无法做到的。
然而重载了()运算符的类的一大缺点就是不够轻量,不管是占用空间还是执行效率上和函数/函数指针都存在差距。
C++11引入了lambda,典型的lambda是这样
int a = 10;auto func = [&a](int b){return a+b;};std::cout << func(10) << ’n’;a = 20;std::cout << func(10) << ’n’;
可以看到lambda可以轻松捕获变量,实现闭包效果,所以我们在很多场合下可以使用lambda来代替重载了()运算符的类。
不过lambda也有局限,首先是它的类型是无法手动写出来的,也就是说对于同样的两个lambda,它的类型是不同的
auto func = [&a](int b){return a+b;};auto func2 = [&a](int b){return a+b;};std::cout << (typeid(func).name() != typeid(func2).name()) << ’n’;
所以我们只能定义的时候只能用auto,而且如果我们想把lambda存到vector、list等容器里,就必须要用包装类std::function,它本质上是个泛型的模板类,也同样重载了()操作符
std::function<int(int)> func3 = func;
使用std::function之后,lambda的轻量优势就消失了,不过有的时候也不得不这么做。