Ali*_*232 22 c++ lambda visual-c++ c++11 visual-studio-2012
这段代码并不是JS开发人员所不知道的
function get_counter()
{
return (
function() {
var c = 0;
return function() { return ++c; };
})();
}
Run Code Online (Sandbox Code Playgroud)
它基本上创建了一个创建不同的枚举器.所以我想知道在使用新的lambda语义的C++ 11中是否可以做同样的事情?我最终写了这篇C++,遗憾的是它不能编译!
int main()
{
int c;
auto a = [](){
int c = 0;
return [&](){
cout << c++;
};
};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以我想知道是否有一个解决方法来编译它,如果有编译器如何使这个代码正确运行?我的意思是它必须创建单独的枚举器,但它也应该收集垃圾(未使用的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
Run Code Online (Sandbox Code Playgroud)
eca*_*mur 20
你的代码有一个错误,它包含一个悬空参考; 的c引用将是指在外部拉姆达局部变量,当外拉姆达返回其将被破坏.
你应该使用mutableby-value lambda capture 来编写它:
auto a = []() {
int c = 0;
return [=]() mutable {
cout << c++;
};
};
Run Code Online (Sandbox Code Playgroud)
这依赖于标准后扩展,允许在返回类型推导lambda中使用多个语句; 如果包含多个语句,是否有理由不允许lambda推断返回类型?解决它的最简单方法是提供一个参数,以便lambda只包含一个语句:
auto a = [](int c) {
return [=]() mutable {
cout << c++;
};
};
Run Code Online (Sandbox Code Playgroud)
不幸的是,lambdas中不允许使用默认参数,因此您必须将其称为a(0).或者以可读性为代价,您可以使用嵌套的lambda调用:
auto a = []() {
return ([](int c) {
return [=]() mutable {
cout << c++;
};
})(0);
};
Run Code Online (Sandbox Code Playgroud)
这种方法的工作方式是,当a执行内部lambda 时,将所有引用的变量复制到其闭包类型的实例中,这里的内容类似于:
struct inner_lambda {
int c;
void operator()() { cout << c++; }
};
Run Code Online (Sandbox Code Playgroud)
然后,外部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};
}
};
Run Code Online (Sandbox Code Playgroud)
如果您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};
}
};
Run Code Online (Sandbox Code Playgroud)
这inner_lambda::c是对本地参数变量的悬空引用c.
一旦变量不再存在,通过引用捕获的lambda不再使用捕获的变量,这是C++的一个自然限制.因此,即使你将它编译,你也不能从它出现的函数返回这个lambda(这也恰好是一个lambda,但这是无关紧要的),因为自动变量c在返回时被销毁.
我认为你需要的代码是:
return [=]() mutable {
cout << c++;
};
Run Code Online (Sandbox Code Playgroud)
我没有测试它,我不知道什么编译器版本支持它,但这是一个按值捕获,mutable也就是说可以通过lambda修改捕获的值.
因此,每次打电话给a你时,都会得到一个不同的计数器,其计数从0开始.每次调用该计数器时,它都会增加自己的副本c.据我所知Javascript(不远),这就是你想要的.
我认为问题在于编译器无法推断出外部lambda(分配给a)的返回类型,因为它包含的不仅仅是简单的一行返回.但不幸的是,也没有办法明确说明内部lambda的类型.所以你必须返回一个std::function,它带来一些额外的开销:
int main()
{
int c;
auto a = []() -> std::function<void()> {
int c = 0;
return [=]() mutable {
std::cout << c++;
};
};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当然,你必须捕捉价值,就像史蒂夫已经在他的回答中解释的那样.
编辑:至于为什么确切的错误是它不能将返回的内部lambda转换为void(*)()(指向void()函数的指针),我只有一些猜测,因为我对他们的lambda实现没有太多的了解,我不确定是完全稳定或符合标准.
但我认为VC至少会尝试推断内部lambda的返回类型,并意识到它返回一个可调用的.但是它以某种方式错误地假设这个内部lambda没有捕获(或者它们无法确定内部lambda的类型),所以它们只是使外部lambda返回一个简单的函数指针,如果内部lambda不会抓住什么.
编辑:和他的评论中的ecatmur状态一样,std::function在创建实际get_counter函数(而不是lambda)时返回a 甚至是必要的,因为正常函数没有任何自动返回类型推导.
| 归档时间: |
|
| 查看次数: |
8431 次 |
| 最近记录: |