问题
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 move
ing 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