検索

ホームページ  >  に質問  >  本文

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

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

ringa_leeringa_lee2773日前415

全員に返信(1)返信します

  • 迷茫

    迷茫2017-04-17 14:37:30

    题目问的其它语言,这个太广泛了,因为编程语言这么多,有形如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的轻量优势就消失了,不过有的时候也不得不这么做。

    返事
    0
  • キャンセル返事