函数返回lambda表达式


88

我想知道是否有可能在C ++ 11中编写一个返回lambda函数的函数。当然,一个问题是如何声明这种功能。每个lambda都有一个类型,但是该类型在C ++中无法表达。我认为这不起作用:

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

也没有:

int(int) retFun();

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


1
补充说一下,无状态lambda函数可转换为函数指针。
snk_kid 2011年

3
IMO,您的第一个选择将无效,因为中的lambdadecltype与函数主体中的lambda不同,因此具有不同的类型(即使您包含return语句)
Motti

1
顺便说一句,如果lambda具有空的capture子句,则可以将其隐式转换为函数指针。
GManNickG 2011年

@GMan:除非您使用的是Visual C ++ 2010或大约一年前(或其前后)发布的g ++版本。直到2010年3月,N3092才添加了capturelesslambda隐式转换为函数指针的功能。
James McNellis 2011年

4
Lambda表达式通常不能出现在未评估的操作数中。因此decltype([](){})sizeof([]() {})无论您在何处书写它,都格式错误。
Johannes Schaub-litb 2011年

Answers:


98

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

此示例返回整数标识函数:

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

6
h!我喜欢StackOverflow。研究该主题本来会花费我更长的时间,然后在StackOverflow上获得答案。谢谢肖恩!
Bartosz Milewski

6
即使在的构造函数中,这也将导致内存分配std::function
Maxim Egorushkin 2011年

2
@Maxim Yegorushkin std :: function具有移动语义,并且可以使用自定义分配器,并且C ++ 0x工作草案包含以下注释: ,其中f的目标是仅包含一个指针或对该对象的引用以及一个成员函数指针的对象。无论如何使用您自己的(池式)分配器。
snk_kid 2011年

1
@Maxim:我的答案是“唯一的解决方案是手工制作函数对象并返回它吗?”
肖恩

2
请记住std::function使用类型擦除,这可能意味着在调用时进行虚拟函数调用的成本std::function。需要注意的一点是,返回的函数是否将在紧密的内部循环中使用,或者在其他情况下使用效率稍低的地方。
安东尼·霍尔

29

对于这个简单的示例,您不需要 std::function

根据标准§5.1.2/ 6:

没有lambda-capturelambda表达式的闭包类型具有指向该函数的指针的公共非虚拟非显式const转换函数,该函数具有与闭包类型的函数调用运算符相同的参数和返回类型。该转换函数返回的值应该是一个函数的地址,该函数在被调用时与调用闭包类型的函数调用操作符具有相同的作用。

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

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

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


1
听起来不错。不幸的是,它不适用于我正在使用的当前编译器:VS2010。std :: function转换可以正常工作。
Bartosz Milewski

3
是的,对于VC2010,此规则的最终措辞来得太晚了。
Ben Voigt

我添加了代码示例。这是完整的程序
jfs

@JFSebastian-在此示例中,lambda的寿命是多少?它足够长的时间才能超过转换为函数指针的结果吗?
Flexo

1
@JFSebastian -我似乎无法找到一个问题的答案,所以我问过它本身就是一个问题:stackoverflow.com/questions/8026170/...
柔印

21

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

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

2
仅当外部lambda仅由return语句组成时,才如此。否则,您必须指定返回类型。
Bartosz Milewski

3
这是最好的答案,因为它不需要std :: function的运行时多态性,并且允许lambda具有非空的捕获列表,但是我将使用const auto fun = ...
robson3.14

2
自C ++ 14起,@ BartoszMilewski不正确。
dzhioev '18

19

尽管该问题专门询问了C ++ 11,但出于其他偶然发现此问题并可以使用C ++ 14编译器的人的考虑,C ++ 14现在允许推导普通函数的返回类型。因此,只需将-> decltype...子句放在函数参数列表之后,就可以调整问题中的示例以使其按需工作:

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

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


4
为什么要提及c ++ 14的推导类型,却省略多态lambda?auto retFun() { return [](auto const& x) { return x; }; }
sehe

1

您应该这样写:

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

得到您的回报作为一个函数,并像这样使用它:

int val = returnFunction(someNumber);

0

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

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();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.