Lambdas只是运算符()重载的类?

Zeb*_*ish 5 c++ lambda function functor

我越是读到关于lambdas的内容越多,我从人们那里听到它们只是伪装的函数对象/函子(除非它们没有捕获任何东西,在这种情况下它们只是自由静态函数.我想在本地写lambdas范围并将它们传递给一个通用事件处理程序,它根据需要调用它们,我开始注意到我几乎无法做任何传统函数对象允许我做的事情.如果我对此的理解是错误的,请告诉我.因为我已经评论过你可以用仿函数做的一大堆东西,据我所知,不能用lambdas:

#include <iostream>
#include <vector>

struct MyFunctorClass
{
    // Custom constructor, can't do with lambda
    MyFunctorClass(int& capturedVariable) : capturedVariable(capturedVariable) 
        { std::cout << "I can do anything on construction.\n"; }

    // Overloading constructors, different ways to initialise function object, can't do with lambda
    MyFunctorClass(int& capturedVariable, int sizeOfBuffer) : capturedVariable(capturedVariable) 
        { heapAllocation = new int[sizeOfBuffer]; }

    // Custom destructor, can't do with lambda
    ~MyFunctorClass() { delete[] heapAllocation; }  

    void operator()() { std::cout << "Standard call\n"; }
    void operator()(int arg) { std::cout << "Argument passed: " << arg << '\n'; }   
    // operator() overloading, different ways to call the function object, can't do with lambda

    int* heapAllocation;                // Have heap allocated resources, can't do with lambda
    bool internalStateVariable = true;  // Initialise a member variable on construction, can't do with lambda
    int& capturedVariable;              // I can access this variable directly with MyFunctorClass::capturedVariable = 7, can't do with lambda
};

int main()
{
    int localVar = 0;
    bool trueOrFalse = false;

    {
        MyFunctorClass* myFunctionObj = new MyFunctorClass(localVar, 100);  
        // Can dynamically allocate function object, can't with lambda
        auto lambda = new[&]() { localVar = 1; };   // Can't do?
        lambda.trueOrFalse = true;      // trueOrFalse isn't member of lambda, even though it captured it, doesn't make sense

    }   // Lambda object is destroyed here. My function object lives until I delete it.

    return 0;
}

void holdFunctionObject(MyFunctorClass* funcObj)
{
    static std::vector<MyFunctorClass*> list;
    list.push_back(funcObj);    
    // I can hold all the function objects forever, they'll never go out of scope unless I delete them, can't do with lambda
}
Run Code Online (Sandbox Code Playgroud)

我感到非常受限制,似乎lambdas只是一种声明"就地"功能的方式.它们也保持状态,但只能保存已经在范围内的对象状态,而不是创建新对象.并且也无法以仿函数的任何特定方式初始化.我做对了吗?因为它们与仅具有重载operator()的类看起来非常不同;

Mat*_*lia 8

我不明白你的意思; 是的,lambdas只是类的实例的语法糖operator(),遵循一些特定的模板1 ; 是的,你不能完成你可以用完全自定义类做的大部分工作 - 并且有充分的理由:如果你想做那些东西,你可以写一个常规的class.

Lambdas采用一种特殊的,广泛使用的模式(可以捕获状态的一次性仿函数)2并给它们非常方便的语法3.如果您需要完全成熟的课程,那么自70年代中期以来,该语言已经涵盖了class关键字.

另外,需要注意的是,虽然在C++ lambda表达式是非常重要的实现在类方面,构成了这一两个概念"的世界观"是不同的; 闭包植根于函数式编程,而不是OOP.它们背后的想法是模拟单个函数(= action)以及在创建站点捕获的工作所需的数据; 而对象首先是一个可变实体,它提供一个变异或查询其状态的接口.闭包可以用对象和对象实现的事实可以用闭包来实现是有趣和有趣的(并且最终来自两者都是状态和代码的组合的事实),但它们从完全不同的理由.使用一个或另一个本质上是理解你想要的主要是代码是否恰好捕获状态("动词")或主要是"打包状态",其中有一个用于改变它的界面("名词").


  1. 这不仅仅是了解其实现的捷径,它们实际上是在标准中的这些术语中指定的.
  2. 来自语言的模式,其中函数和闭包从一开始就是第一类对象,因此产生的语法糖有助于编写从这些语言变异的样式的代码.
  3. 我告诉你,这是一种祝福; 由于不得不将C++ 11代码移植到C++ 03,所以去除代码是一种令人沮丧的练习 - 一切都变得如此繁琐冗长,并且在阅读时结果"流动"的方式更糟.捕获状态特别可怕,通常你必须至少重复四次相同的事情 - 一个在类级别的字段声明中,一个在构造函数原型中,一个在初始化列表中,一个在构造函数调用中.与lambda捕获列表比较.


Nic*_*las 6

除非它们没有捕获任何东西,在这种情况下它们只是自由静态函数

这实际上是不正确的.捕捉少lambda表达式可以被转换成函数指针,但他们一样的东西"自由静态功能".它们仍然是对象,而静态函数则不是(尽管你可以指向函数,但这并不能使函数成为C++意义上的对象).

至于其余部分,你似乎没有认识到"所有lambdas都是仿函数"和"所有仿函数都是lambdas"之间的区别.前者是真实的,后者是不是,也不是后者为真.

Lambdas私下拥有自己的州.手写的仿函数可以随心所欲地保持状态.大多数人会认为具有可公开访问状态的仿函数是不好的形式,但如果这是你想要的,那么这是一个由你决定的决定.

通过使用lambda,您同意接受其限制.lambda的类型在声明点处被合成,并且它没有被命名,因此你不能创建接受特定lambda的API(好吧,也许你可以,但你不应该).如果您希望用户传递特定的仿函数类型,那么您不应该使用lambda.

但实际上......您经常需要用户传递特定的仿函数类型?大多数情况下,您需要的是让用户传递可以使用特定签名调用的内容.这std::function是为了什么.或者,如果它是模板函数,则可以使用可使用特定签名调用的任意对象.

如果你确实需要用户传递特定的仿函数类型......那就这样吧.但由于大多数接口不会受到如此限制,因此lambda是一种有效的解决方案.

Lambdas的核心是语法糖,可以很容易地编写常见的函子.肯定会有一些lambda无法满足您的需求.但由于lambda涵盖了大约90%的用途,这使得它们足够好.

lambdas的主要目的是逻辑地将某个进程的代码放在您要调用该进程的位置附近.这提供了相对于该进程的代码的位置.这对于回调,延续等是常见的.

与手写的仿函数相比,它还允许此类代码轻松访问回调的本地范围.这使得这些过程成为代码的一部分,而不是必须跟踪仿函数类型的定义并阅读其operator()实现以找出正在发生的事情.


也:

auto lambda = new[&]() { localVar = 1; };   // Can't do?
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

auto lambda = new auto([&]() { localVar = 1; });   // Can't do?
Run Code Online (Sandbox Code Playgroud)