C++ 中的可调用对象与其他编程语言中的函数对象有何异同?

查看:69
本文介绍了C++ 中的可调用对象与其他编程语言中的函数对象有何异同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

C++ 中的可调用对象与其他编程语言中的函数对象有何异同?

解决方案

题目问的其它语言,这个太广泛了,因为编程语言这么多,有形如C语言这种传统的函数,也有形如java那样无法独立存在的函数,还有各类脚本语言的不同形式的函数。

不过在函数是第一等公民的语言里,要求函数能做到

  • 函数可以独立存在,且可在任意地方执行

  • 函数的作用域是词法作用域,且可捕获外部变量

  • 函数可从另外一个函数返回,也可以作为别的函数的参数(高阶函数)

  • 函数从别的函数生成后,所引用的作用域依旧可访问,而不是被销毁

在不少脚本语言和新兴语言里,函数大都是有以上特性表现的。

关于不同语言闭包捕获的问题,可以参考一下我之前写过的一篇文章 https://zhuanlan.zhihu.com/p/...

再说C++,C++有四个可以用来调用的东西

  1. 函数

  2. 函数指针

  3. 重载了()运算符的类

  4. 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的轻量优势就消失了,不过有的时候也不得不这么做。

这篇关于C++ 中的可调用对象与其他编程语言中的函数对象有何异同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆