Moving a lambda: once you've move-captured a move-only type, how can the lambda be used? [duplicate]

徘徊边缘 提交于 2020-02-12 11:53:16

问题


This answer explains how to move-capture a variable within a lambda in C++14.

But once you've move-captured an un-copyable object (such as a std::unique_ptr) within a lambda, you cannot copy the lambda itself.

This would be fine if you could move the lambda, but I get a compile error when trying to do so:

using namespace std;

class HasCallback
{
  public:
    void setCallback(std::function<void(void)>&& f)
    {
      callback = move(f);
    }

    std::function<void(void)> callback;
};

int main()
{
  auto uniq = make_unique<std::string>("Blah blah blah");
  HasCallback hc;
  hc.setCallback(
      [uniq = move(uniq)](void)
      {
        std::cout << *uniq << std::endl;
      });

  hc.callback();
}

This produces the following error with g++ (I've attempted to copy only the relevant line):

error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’

...implying, I think, that my attempt to move the lambda has failed.

clang++ gives a similar error.

I tried explicitly moveing the lambda (even though it's a temporary value), but that did not help.

EDIT: The answers below adequately address the compile errors produced by the above code. For an alternate approach, simply release the unique pointer's target value into a std::shared_ptr, which can be copied. (I'm not writing this as an answer, because that would assume that this is an XY problem, but the underlying reason why unique_ptr can't be used in a lambda that gets converted to a std::function is important to understand.)

EDIT 2: Hilariously enough, I just realized auto_ptr would actually do the right thing here (!), as far as I can tell. It acts essentially like unique_ptr, but allows copy-construction in place of move-construction.


回答1:


You can move the lambda, that's fine. That's not what your problem is though, you're trying to instantiate a std::function with a noncopyable lambda. And the:

template< class F > 
function( F f );

constructor of function does:

5) Initializes the target with a copy of f.

This is because std::function:

satisfies the requirements of CopyConstructible and CopyAssignable.

Since function has to be copyable, everything you put into it must also be copyable. And a move-only lambda does not meet that requirement.




回答2:


std::function is not a lambda! It's a wrapper that can be constructed from any type of callable, including a lambda. std::function requires that the callable be copy-constructible, which is why your example fails.

A move-only lambda can be moved again as shown below.

template<typename F>
void call(F&& f)
{
    auto f1 = std::forward<F>(f);  // construct a local copy
    f1();
}

int main()
{
  auto uniq = make_unique<std::string>("Blah blah blah");
  auto lambda = [uniq = move(uniq)]() {
        std::cout << *uniq << std::endl;
      };
//  call(lambda);   // doesn't compile because the lambda cannot be copied
  call(std::move(lambda));
}

Live demo



来源:https://stackoverflow.com/questions/32486623/moving-a-lambda-once-youve-move-captured-a-move-only-type-how-can-the-lambda

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!