【问题标题】:Function returning a lambda expression返回 lambda 表达式的函数
【发布时间】:2011-06-11 05:42:05
【问题描述】:

我想知道是否可以在 C++11 中编写一个返回 lambda 函数的函数。当然,一个问题是如何声明这样的函数。每个 lambda 都有一个类型,但该类型在 C++ 中是不可表达的。我认为这行不通:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

也不是这样:

int(int) retFun();

我不知道有任何从 lambda 到函数指针等的自动转换。唯一的解决方案是手工制作函数对象并返回它吗?

【问题讨论】:

  • 补充一下已经说过的,无状态的 lambda 函数可以转换为函数指针。
  • IMO 您的第一个选项将不起作用,因为 decltype 中的 lambda 与函数体中的不同,因此具有不同的类型(即使您包含 return 语句)
  • 顺便说一句,如果 lambda 有一个空的捕获子句,它可以隐式转换为指向函数的指针。
  • @GMan:除非您使用 Visual C++ 2010 或大约一年前(或大约)发布的 g++ 版本。直到 2010 年 3 月在 N3092 中才添加了到函数指针的无捕获 lambda 隐式转换。
  • Lambda 表达式一般不能出现在未计算的操作数中。所以decltype([](){})sizeof([]() {}) 无论你写在哪里都是格式错误的。

标签: c++ function c++11 lambda


【解决方案1】:

您不需要手工制作的函数对象,只需使用 std::function,lambda 函数可以转换为:

此示例返回整数恒等函数:

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}

【讨论】:

  • 这将在std::function的构造函数中导致内存分配。
  • @Maxim Yegorushkin std::function 具有移动语义,而且它可以使用自定义分配器,C++0x 工作草案有这些注释:“[注意:鼓励实现避免使用动态分配小型可调用对象的内存,例如,其中 f 的目标是一个对象,它只包含一个指针或对一个对象的引用和一个成员函数指针。-结束注释]" 所以基本上你不能对特定的分配策略做出很多假设实现正在使用,但无论如何您应该能够使用自己的(池化)分配器。
  • @Sean:你也可以把它包装成 boost::any。问题是如何指定返回类型。这个答案回避了这个问题。
  • @Maxim:我的回答是“唯一的解决方案是手工制作函数对象并返回它吗?”
  • 请记住std::function 使用类型擦除,这可能意味着调用std::function 时进行虚函数调用的成本。如果返回的函数将用于紧密的内部循环或其他轻微低效率问题的上下文中,需要注意的事项。
【解决方案2】:

对于这个简单的例子,你不需要std::function

来自标准 §5.1.2/6:

没有 lambda-capturelambda-expression 的闭包类型有一个公共的非虚拟非显式 const 转换函数,指向具有相同参数的函数的指针并返回类型作为闭包类型的函数调用运算符。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

由于您的函数没有捕获,这意味着 lambda 可以转换为指向 int (*)(int) 类型函数的指针:

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

这是我的理解,如果我错了,请纠正我。

【讨论】:

  • 这听起来不错。不幸的是,它不适用于我正在使用的当前编译器:VS 2010。std::function 转换恰好工作。
  • 是的,这条规则的最终措辞对于 VC2010 来说来得太晚了。
  • 我添加了代码示例。这是full program
  • @J.F.Sebastian - 这个例子中 lambda 的生命周期是多少?是否足够长于转换为函数指针的结果?
  • @J.F.Sebastian - 我似乎无法找到答案,所以我将其作为一个问题提出:stackoverflow.com/questions/8026170/…
【解决方案3】:

虽然该问题专门询问了 C++11,但为了其他偶然发现此问题并可以访问 C++14 编译器的人,C++14 现在允许为普通函数推导返回类型。因此,只需在函数参数列表后删除 -&gt; decltype... 子句,即可调整问题中的示例以使其按需要工作:

auto retFun()
{
    return [](int x) { return x; }
}

但是请注意,如果函数中出现多个return &lt;lambda&gt;;,这将不起作用。这是因为返回类型推导的一个限制是所有返回语句必须返回相同类型的表达式,但每个 lambda 对象都由编译器赋予其自己唯一的类型,因此return &lt;lambda&gt;; 表达式将各自具有不同的类型。

【讨论】:

  • 为什么提到 c++14 的推导类型却忽略了多态 lambda? auto retFun() { return [](auto const&amp; x) { return x; }; }
【解决方案4】:

您可以从其他 lambda 函数返回 lambda 函数,因为您不应显式指定 lambda 函数的返回类型。只需在全局范围内编写类似的内容:

 auto retFun = []() {
     return [](int x) {return x;};
 };

【讨论】:

  • 只有当外层 lambda 只包含 return 语句时才会这样。否则你必须指定返回类型。
  • 这是最好的答案,因为它不需要 std::function 的运行时多态性并且允许 lambda 有一个非空的捕获列表,但是我会使用 const 自动乐趣 = ...
  • @BartoszMilewski 自 C++14 以来不正确。
【解决方案5】:

你应该这样写:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

将你的回报作为一个函数,并像这样使用它:

int val = returnFunction(someNumber);

【讨论】:

    【解决方案6】:

    例如,如果您没有 c++ 11 并且在微控制器上运行您的 c++ 代码。您可以返回一个 void 指针,然后执行强制转换。

    void* functionThatReturnsLambda()
    {
        void(*someMethod)();
    
        // your lambda
        someMethod = []() {
    
            // code of lambda
    
        };
    
        return someMethod;
    }
    
    
    int main(int argc, char* argv[])
    {
    
        void* myLambdaRaw = functionThatReturnsLambda();
    
        // cast it
        auto myLambda = (void(*)())myLambdaRaw;
    
        // execute lambda
        myLambda();
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    相关资源
    最近更新 更多