在C ++ 11 lambda语法中,堆分配闭包? [英] In C++11 lambda syntax, heap-allocated closures?

查看:117
本文介绍了在C ++ 11 lambda语法中,堆分配闭包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++ 11 lambdas是伟大的!



但有一件事是缺失,这是如何安全地处理可变数据。



以下将在第一次计数后给出不良计数:

  #include< cstdio& 
#include< functional>
#include< memory>

std :: function< int(void)> f1()
{
int k = 121;
return std :: function< int(void)>([&] {return k ++;});
}

int main()
{
int j = 50;
auto g = f1();
printf(%d \\\
,g());
printf(%d \\\
,g());
printf(%d \\\
,g());
printf(%d \\\
,g());
}

给予

  $ g ++  -  4.5 -std = c ++ 0x -o test test.cpp&& ./test 
121
8365280
8365280
8365280



<原因是在 f1()返回后, k 超出范围,但仍在堆栈上。所以,第一次执行 g() k 很好,但之后堆栈已损坏, k 失去其价值。



因此,我在C ++ 11中设法安全可返回闭包的唯一方法是在堆上显式分配封闭变量:

  std :: function< int(void)> f2()
{
int k = 121;
std :: shared_ptr< int> o = std :: shared_ptr< int>(new int(k));
return std :: function< int(void)>([=] {return(* o)++;});
}

int main()
{
int j = 50;
auto g = f2()
printf(%d \\\
,g());
printf(%d \\\
,g());
printf(%d \\\
,g());
printf(%d \\\
,g());
}

这里, [=] 用于确保共享指针被复制,不被引用,以便内存处理正确完成: k 应该在生成的函数 g 超出作用域时释放。结果符合需要,

  $ g ++  -  4.5 -std = c ++ 0x -o test test.cpp&& ; ./test 
121
122
123
124

通过解引用来引用变量是非常难看的,但应该可以使用引用:

  std :: function< int(void)> f3()
{
int k = 121;
std :: shared_ptr< int> o = std :: shared_ptr< int>(new int(k));
int& p = * o;
return std :: function< int(void)>([&] {return p ++;});
}

其实这奇怪的给我,

  $ g ++  -  4.5 -std = c ++ 0x -o test test.cpp&& ./test 
0
1
2
3



<任何想法为什么?也许不是有礼貌的参考一个共享的指针,现在我想,因为它不是一个被跟踪的引​​用。我发现将引用移动到lambda内部导致崩溃,

  std :: function< int(void)> f4()
{
int k = 121;
std :: shared_ptr< int> o = std :: shared_ptr< int>(new int(k));
return std :: function< int(void)>([&] {int& p = * o; return p ++;});
}

给予,

  g ++  -  4.5 -std = c ++ 0x -o test test.cpp&&& ./test 
156565552
/ bin / bash:line 1:25219分段错误./test

在任何情况下,如果有一种方法自动安全可返回闭包通过堆分配是很好。例如,如果有一个替代 [=] [&] 表明变量应该是heap通过对共享指针的引用来分配和引用。我最初的想法,当我学到 std :: function 是它创建一个封装闭包的对象,因此它可以为闭包环境提供存储,但我的实验表明,似乎没有帮助。



我认为安全可返回的闭包在C + + 11将是最重要的使用它们,有没有人知道如何可以实现更多

f1 中,您将获得未定义的行为原因你说; lambda包含对局部变量的引用,并且在函数返回后,引用不再有效。为了解决这个问题,你不必在堆上分配,你只需要声明捕获的值是可变的:

  int k = 121; 
return std :: function< int(void)>([=]()mutable {return k ++;});

你必须小心使用这个lambda,因为不同的副本会修改他们自己捕获的变量的副本。通常算法期望使用函子的副本等效于使用原始函数。我认为只有一个算法实际上允许有状态函数对象std :: for_each,它返回函数对象的另一个副本,所以你可以访问发生的任何修改。






f3 中没有任何内容维护共享指针的副本,因此内存被释放并访问给出未定义的行为。您可以通过显式地捕获共享指针的值来解决这个问题,并仍然通过引用捕获指向的int。

  std :: shared_ptr< int> o = std :: shared_ptr< int>(new int(k)); 
int& p = * o;
return std :: function< int(void)>([& p,o] {return p ++;});

f4 再次捕获对局部变量 o 的引用。您应该简单地通过值捕获,但是仍然在lambda中创建 int& p ,以获得所需的语法。

  std :: shared_ptr< int> o = std :: shared_ptr< int>(new int(k)); 
return std :: function< int(void)>([o]() - > int {int& p = * o; return p ++;});

请注意,当添加第二个语句时,C ++ 11不再允许您省略返回类型。 (clang,我假设gcc有一个扩展,允许返回类型扣除甚至与多个语句,但你应该至少得到一个警告。)


C++11 lambdas are great!

But one thing is missing, which is how to safely deal with mutable data.

The following will give bad counts after the first count:

#include <cstdio>
#include <functional>
#include <memory>

std::function<int(void)> f1()
{
    int k = 121;
    return std::function<int(void)>([&]{return k++;});
}

int main()
{
    int j = 50;
    auto g = f1();
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
}

gives,

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
8365280
8365280
8365280

The reason is that after f1() returns, k is out of scope but still on the stack. So the first time g() is executed k is fine, but after that the stack is corrupted and k loses its value.

So, the only way I've managed to make safely returnable closures in C++11 is to allocate closed variables explicitly on the heap:

std::function<int(void)> f2()
{
    int k = 121;
    std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    return std::function<int(void)>([=]{return (*o)++;});
}

int main()
{
    int j = 50;
auto g = f2();
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
}

Here, [=] is used to ensure the shared pointer is copied, not referenced, so that memory handling is done correctly: the heap-allocated copy of k should be freed when the generated function g goes out of scope. The result is as desired,

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
122
123
124

It's pretty ugly to refer to variables by dereferencing them, but it should be possible to use references instead:

std::function<int(void)> f3()
{
    int k = 121;
    std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    int &p = *o;
    return std::function<int(void)>([&]{return p++;});
}

Actually, this oddly gives me,

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
0
1
2
3

Any idea why? Maybe it's not polite to take a reference of a shared pointer, now that I think about it, since it's not a tracked reference. I found that moving the reference to inside the lambda causes a crash,

std::function<int(void)> f4()
{
    int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    return std::function<int(void)>([&]{int &p = *o; return p++;});
}

giving,

g++-4.5 -std=c++0x -o test test.cpp && ./test
156565552
/bin/bash: line 1: 25219 Segmentation fault      ./test

In any case, it would be nice if there was a way to automatically make safely returnable closures via heap allocation. For example, if there was an alternative to [=] and [&] that indicated that variables should be heap allocated and referenced via references to shared pointers. My initial thought when I learned about std::function was that it creates an object encapsulating the closure, therefore it could provide storage for the closure environment, but my experiments show that this doesn't seem to help.

I think safely returnable closures in C++11 are going to be paramount to using them, does anyone know how this can be accomplished more elegantly?

解决方案

In f1 you're getting undefined behavior for the reason you say; the lambda contains a reference to a local variable, and after the function returns the reference is no longer valid. To get around this you don't have to allocate on the heap, you simply have to declare that captured values are mutable:

int k = 121;
return std::function<int(void)>([=]() mutable {return k++;});

You will have to be careful about using this lambda though, because different copies of it will be modifying their own copy of the captured variable. Often algorithms expect that using a copy of a functor is equivalent to using the original. I think there's only one algorithm that actually makes allowances for a stateful function object, std::for_each, where it returns another copy of the function object it uses so you can access whatever modifications occurred.


In f3 nothing is maintaining a copy of the shared pointer, so the memory is being freed and accessing it gives undefined behavior. You can fix this by explicitly capturing the shared pointer by value and still capture the pointed-to int by reference.

std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
int &p = *o;
return std::function<int(void)>([&p,o]{return p++;});

f4 is again undefined behavior because you're again capturing a reference to a local variable, o. You should simply capture by value but then still create your int &p inside the lambda in order to get the syntax you want.

std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
return std::function<int(void)>([o]() -> int {int &p = *o; return p++;});

Note that when you add the second statement C++11 no longer allows you to omit the return type. (clang and I assume gcc have an extension that allows return type deduction even with multiple statement, but you should get a warning at least.)

这篇关于在C ++ 11 lambda语法中,堆分配闭包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆