2011-11-23 132 views
30

我曾尝试以下:如何将unique_ptr捕获到lambda表达式中?

std::function<void()> getAction(std::unique_ptr<MyClass> &&psomething){ 
    //The caller given ownership of psomething 
    return [psomething](){ 
     psomething->do_some_thing(); 
     //psomething is expected to be released after this point 
    }; 
} 

不过,这并不编译。有任何想法吗?

UPDATE:

的建议,一些新的语法需要明确指定,我们需要将所有权转移给拉姆达,我现在想的语法如下:

std::function<void()> getAction(std::unique_ptr<MyClass> psomething){ 
    //The caller given ownership of psomething 
    return [auto psomething=move(psomething)](){ 
     psomething->do_some_thing(); 
     //psomething is expected to be released after this point 
    }; 
} 

难道是一个好的候选人?

更新1:

我会告诉我的执行movecopy如下:

template<typename T> 
T copy(const T &t) { 
    return t; 
} 

//process lvalue references 
template<typename T> 
T move(T &t) { 
    return std::move(t); 
} 

class A{/*...*/}; 

void test(A &&a); 

int main(int, char **){ 
    A a; 
    test(copy(a)); //OK, copied 
    test(move(a)); //OK, moved 
    test(A());  //OK, temporary object 
    test(copy(A())); //OK, copying temporary object 
    //You can disable this behavior by letting copy accepts T & 
    //test(move(A())); You should never move a temporary object 
    //It is not good to have a rvalue version of move. 
    //test(a); forbidden, you have to say weather you want to copy or move 
    //from a lvalue reference. 
} 

回答

46

此问题是由lambda generalized capture在C++ 14解决:

// a unique_ptr is move-only 
auto u = make_unique<some_type>(some, parameters); 

// move the unique_ptr into the lambda 
go.run([u = move(u)]{do_something_with(u);}); 
+0

这是理想的解决方案。 –

+0

在这种情况下,何时发布唯一指针?当lambda呢? – Leo

+0

@Leo必须将唯一指针移动到lambda的数据块(lambda是带有(())运算符的对象),然后在释放lambda对象后释放。如果lambda对象没有移动,则可以在'go.run'之后释放作为右值的lambda对象。如果它被移动,它可以在任何时候被释放,如果你想安排'do_something_with'稍后运行,这是有意义的。 –

31

不能永久性的拉姆达捕捉unique_ptr。事实上,如果你想永久捕获任何一个lambda,它必须是可复制的;只是可移动性不足。

这可能被认为是C++ 11中的一个缺陷,但您需要一些语法来明确说明您想将unique_ptr值移入lambda。 C++ 11规范非常谨慎,以防止对命名变量进行隐式移动;这就是为什么std::move存在,这是一个的事情。

要做你想做的事情,需要使用std::bind(这将是半复杂的,需要一个短序列binds)或者只是返回一个普通的旧对象。

另外,从来没有采取unique_ptr&&,除非你实际上是在写它的移动构造函数。只要看看它的价值;用户可以通过价值提供的唯一方式是使用std::move。事实上,除非你正在编写移动构造函数/赋值操作符(或者实现一个转发函数),否则通常不会采用&&中的任何内容。

+0

Greate!如果您提供了一些“绑定”序列的示例,我想将其标记为正确的答案。 –

+2

我同意'&&'采取'unique_ptr'不是一个好主意。但是,总的来说,我相信'&&'带来的东西意味着“我希望调用者实际上给了我东西的所有权,而不仅仅是为了参考”。 –

+0

@EarthEngine:你可以通过* value *参数来获得。如果他们通过一个临时的,那么这个临时的会被移入你的参数值。如果他们通过一个非临时的,他们仍然必须使用'std :: move',这会导致移动发生在你的*参数*中。你这样做的方式意味着你的功能没有*拥有它的所有权。 [我的帖子在这里](http://stackoverflow.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function/8114913#8114913)更详细地解释了这一点。 –

18

“半曲” 的解决方案使用std::bind由尼科尔流星锤提到的并没有那么糟糕毕竟:

std::function<void()> getAction(std::unique_ptr<MyClass>&& psomething) 
{ 
    return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); }, 
        std::move(psomething)); 
} 
+2

我同意,看起来不错。 –

+1

不能编译在c + + 11 – ZivS

+0

由于std ::函数需要可复制,我看不出这是如何工作的。 – Catskul

7

为我工作的次优解决方案是将unique_ptr转换为shared_ptr,然后在lambda中捕获shared_ptr

std::function<void()> getAction(std::unique_ptr<MyClass> psomething) 
{ 
    //The caller given ownership of psomething 
    std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething)); 
    return [psomethingShared]() 
    { 
     psomethingShared->do_some_thing(); 
    }; 
} 
0

我用这个真的狡猾的解决办法,其中涉及的坚持一个unique_ptr里面shared_ptr。这是因为我的代码需要unique_ptr(由于API限制),所以我实际上无法将其转换为shared_ptr(否则我永远无法将我的unique_ptr拿回)。

我使用这种憎恶的理由是,它是用于我的测试代码,并且我必须将std::bind一个unique_ptr纳入测试函数调用。

// Put unique_ptr inside a shared_ptr 
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique)); 

std::function<void()> fnTest = std::bind([this, sh, input, output]() { 
    // Move unique_ptr back out of shared_ptr 
    auto unique = std::move(*sh.get()); 

    // Make sure unique_ptr is still valid 
    assert(unique); 

    // Move unique_ptr over to final function while calling it 
    this->run_test(std::move(unique), input, output); 
}); 

现在呼吁fnTest()将调用run_test()在经过unique_ptr它。第二次调用fnTest()将导致断言失败,因为unique_ptr已在第一次调用期间移动/丢失。