首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++11 lambda返回lambda

C++11 lambda返回lambda
EN

Stack Overflow用户
提问于 2012-09-28 19:58:02
回答 6查看 10.2K关注 0票数 25

这段代码对JS开发人员来说并不陌生

代码语言:javascript
复制
function get_counter()
{
    return (
        function() {
            var c = 0;
            return function() { return ++c; };
        })();
}

它基本上创建了一个,用于创建不同的枚举数。所以我想知道在C++11中是否可以用新的lambda语义来做同样的事情?我最终写了这段C++,不幸的是它不能编译!

代码语言:javascript
复制
int main()
{
    int c;
    auto a = [](){
        int c = 0;
        return [&](){
            cout << c++;
        };
    };
    return 0;
}

所以我想知道是否有解决办法来编译它,如果有的话,编译器如何使这段代码正确运行?我的意思是它必须创建单独的枚举器,但它也应该收集垃圾(未使用的c变量)。

顺便说一句,我使用的是VS2012编译器,它生成了这个错误:

代码语言:javascript
复制
Error   2   error C2440: 'return' : cannot convert from 'main::<lambda_10d109c73135f5c106ecbfa8ff6f4b6b>::()::<lambda_019decbc8d6cd29488ffec96883efe2a>' to 'void (__cdecl *)(void)'    c:\users\ali\documents\visual studio 2012\projects\test\test\main.cpp   25  1   Test
EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-09-28 20:14:08

您的代码有一个错误,因为它包含一个悬空引用;c引用将引用外部lambda中的局部变量,当外部lambda返回时,该变量将被销毁。

您应该使用mutable按值的lambda捕获来编写它:

代码语言:javascript
复制
auto a = []() {
    int c = 0;
    return [=]() mutable {
        cout << c++;
    };
};

这依赖于后标准扩展,以允许在返回类型推导的lambda中有多个语句;Is there a reason on not allowing lambdas to deduce the return type if it contains more than one statement?修复它的最简单的方法是提供一个参数,以便lambda只包含一个语句:

代码语言:javascript
复制
auto a = [](int c) {
    return [=]() mutable {
        cout << c++;
    };
};

不幸的是,lambda中不允许使用默认参数,因此您必须将其称为a(0)。或者,以可读性为代价,您可以使用嵌套的lambda调用:

代码语言:javascript
复制
auto a = []() {
    return ([](int c) {
        return [=]() mutable {
            cout << c++;
        };
    })(0);
};

其工作方式是,当a执行时,内部lambda将所有引用的变量复制到其闭包类型的实例中,如下所示:

代码语言:javascript
复制
struct inner_lambda {
    int c;
    void operator()() { cout << c++; }
};

闭包类型的实例随后由外部lambda返回,可以调用该实例,并在被调用时修改其c副本。

总而言之,您的(固定)代码将转换为:

代码语言:javascript
复制
struct outer_lambda {
    // no closure
    struct inner_lambda {
        int c;    // by-value capture
        // non-const because "mutable"
        void operator()() { cout << c++; }
    }
    // const because non-"mutable"
    inner_lambda operator()(int c) const {
        return inner_lambda{c};
    }
};

如果您将c保留为按引用捕获,则这将是:

代码语言:javascript
复制
struct outer_lambda {
    // no closure
    struct inner_lambda {
        int &c;    // by-reference capture
        void operator()() const { cout << c++; } // const, but can modify c
    }
    inner_lambda operator()(int c) const {
        return inner_lambda{c};
    }
};

这里的inner_lambda::c是对本地参数变量c的悬空引用。

票数 21
EN

Stack Overflow用户

发布于 2012-09-28 20:11:04

通过引用捕获的lambda不能再使用捕获的变量,这是C++的一个自然限制,一旦该变量不再存在。所以即使你编译了它,你也不能从它出现的函数中返回这个lambda (这也是一个lambda,但这是无关紧要的),因为自动变量c在返回时会被销毁。

我认为你需要的代码是:

代码语言:javascript
复制
return [=]() mutable {
    cout << c++;
};

我还没有测试过它,我也不知道哪个编译器版本支持它,但这是按值捕获的,mutable表示捕获的值可以由lambda修改。

因此,每次调用a时,您都会得到一个不同的计数器,它自己的计数从0开始。每次调用该计数器时,它都会递增自己的c副本。据我所知,Javascript (不远)就是你想要的。

票数 9
EN

Stack Overflow用户

发布于 2012-09-28 20:19:43

我认为问题在于编译器无法推断外部lambda (赋值给a)的返回类型,因为它包含的不仅仅是一个简单的一行返回。但不幸的是,也没有办法显式地声明内部lambda的类型。因此,您必须返回一个std::function,这会带来一些额外的开销:

代码语言:javascript
复制
int main()
{
    int c;
    auto a = []() -> std::function<void()> {
        int c = 0;
        return [=]() mutable {
            std::cout << c++;
        };
    };
    return 0;
}

当然,您必须按值捕获,就像Steve在他的回答中所解释的那样。

EDIT:确切的错误是它不能将返回的内部lambda转换为void(*)() (指向void()函数的指针),我只有一些猜测,因为我对他们的lambda实现没有太多的洞察力,我根本不确定它是不是很稳定或符合标准。

但我认为VC至少尝试推导出内部lambda的返回类型,并意识到它返回一个可调用的。但不知何故,它错误地假设这个内部lambda不能捕获(或者它们不能确定内部lambda的类型),所以它们只是让外部lambda返回一个简单的函数指针,如果内部lambda不能捕获任何东西,这确实可以工作。

编辑:和他评论中的ecatmur状态一样,在创建一个实际的std::function函数(而不是get_counter函数)时,返回一个lambda甚至是必要的,因为普通函数没有任何自动的返回类型推导。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12639578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档