这段代码对JS开发人员来说并不陌生
function get_counter()
{
return (
function() {
var c = 0;
return function() { return ++c; };
})();
}它基本上创建了一个,用于创建不同的枚举数。所以我想知道在C++11中是否可以用新的lambda语义来做同样的事情?我最终写了这段C++,不幸的是它不能编译!
int main()
{
int c;
auto a = [](){
int c = 0;
return [&](){
cout << c++;
};
};
return 0;
}所以我想知道是否有解决办法来编译它,如果有的话,编译器如何使这段代码正确运行?我的意思是它必须创建单独的枚举器,但它也应该收集垃圾(未使用的c变量)。
顺便说一句,我使用的是VS2012编译器,它生成了这个错误:
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发布于 2012-09-28 20:14:08
您的代码有一个错误,因为它包含一个悬空引用;c引用将引用外部lambda中的局部变量,当外部lambda返回时,该变量将被销毁。
您应该使用mutable按值的lambda捕获来编写它:
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只包含一个语句:
auto a = [](int c) {
return [=]() mutable {
cout << c++;
};
};不幸的是,lambda中不允许使用默认参数,因此您必须将其称为a(0)。或者,以可读性为代价,您可以使用嵌套的lambda调用:
auto a = []() {
return ([](int c) {
return [=]() mutable {
cout << c++;
};
})(0);
};其工作方式是,当a执行时,内部lambda将所有引用的变量复制到其闭包类型的实例中,如下所示:
struct inner_lambda {
int c;
void operator()() { cout << c++; }
};闭包类型的实例随后由外部lambda返回,可以调用该实例,并在被调用时修改其c副本。
总而言之,您的(固定)代码将转换为:
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保留为按引用捕获,则这将是:
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的悬空引用。
发布于 2012-09-28 20:11:04
通过引用捕获的lambda不能再使用捕获的变量,这是C++的一个自然限制,一旦该变量不再存在。所以即使你编译了它,你也不能从它出现的函数中返回这个lambda (这也是一个lambda,但这是无关紧要的),因为自动变量c在返回时会被销毁。
我认为你需要的代码是:
return [=]() mutable {
cout << c++;
};我还没有测试过它,我也不知道哪个编译器版本支持它,但这是按值捕获的,mutable表示捕获的值可以由lambda修改。
因此,每次调用a时,您都会得到一个不同的计数器,它自己的计数从0开始。每次调用该计数器时,它都会递增自己的c副本。据我所知,Javascript (不远)就是你想要的。
发布于 2012-09-28 20:19:43
我认为问题在于编译器无法推断外部lambda (赋值给a)的返回类型,因为它包含的不仅仅是一个简单的一行返回。但不幸的是,也没有办法显式地声明内部lambda的类型。因此,您必须返回一个std::function,这会带来一些额外的开销:
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甚至是必要的,因为普通函数没有任何自动的返回类型推导。
https://stackoverflow.com/questions/12639578
复制相似问题