#include <iostream>
#include <memory>
#include <functional>
struct PacketFrom
{
typedef std::unique_ptr<PacketFrom> SPtr;
};
template<class P>
class R{
public:
void queue_read(P *t)
{
doSomething([t = typename PacketFrom::SPtr(t)](){test(std::move(t));});
}
void doSomething(std::function<void()> f) {
}
void test(PacketFrom::SPtr p) {
}
};
int main()
{
R<PacketFrom> r;
return 0;
}
How can this code work if the lambda function is being passed by copy? The lambda owns a unique_ptr
which cannot be copied. I thought it was simply not capturing the unique_ptr so I added test()
just to be sure it was capturing. However the code compiles fine.
"Moving" transfers ownership to a new unique_ptr and resets the old unique_ptr .
Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions (via pointers thereto), lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
std::unique_ptr. std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
This code doesn't actually "work" in that sense. It compiles, but only because you don't ever call queue_read
.
If you try to use it (which forces the compiler to actually instantiate the R<P>::queue_read
, which it doesn't have to do until that point) then you get an error from every compiler:
template<class P>
class R{
public:
void queue_read(P *t)
{
doSomething([this, t = std::unique_ptr<P>(t)]() { test(std::move(t)); });
}
void doSomething(std::function<void()> f) {
(void)f;
}
void test(std::unique_ptr<P> p) {
(void)p;
}
};
int main()
{
R<int> r;
r.queue_read(new int(1));
return 0;
}
clang 9.0:
error: call to deleted constructor of 'std::unique_ptr<int>' doSomething([this, t = std::unique_ptr<P>(t)]() { test(std::move(t)); });
gcc 9.2:
error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]' 11 | doSomething([this, t = std::unique_ptr<P>(t)]() { test(std::move(t)); });
MSVC 19.22:
error C2280: 'std::unique_ptr<P,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
https://godbolt.org/z/KmjVJB (thanks Richard!)
Again, the key here is that the compiler didn't actually compile the code of queue_read
because there was no need. The function is implicitly inline
by virtue of being defined within the class body. Instantiating R<P>
for some P
causes only the declarations, but not the definitions of its member functions to be instantiated. Only once you actually call queue_read
does the compiler have to complain.
This is a good thing by the way. You can use std::vector<MoveOnlyType>
and do everything that doesn't require copying, even though some of the std::vector
member functions require a copyable type. But as long as you never use the latter functions, everything is fine. If the compiler always instantiated all the member function definitions and reported errors in those (even if never used) it would be way more cumbersome.
How can std::function be copied if it captures an unique_ptr?
A std::function
doesn't capture anything.
A lambda that captures a non-copyable object such as std::unique_ptr
is itself non-copyable. Such lambda doesn't satisfy, nor does any other non-copyable function object type satisfy the requirements of std::function
which requires the functor to be copyable. Standard rule (from latest draft):
[func.wrap.func.con]
template<class F> function(F f);
Requires: F shall be Cpp17CopyConstructible.
However the code compiles fine.
This is typical when the ill-formed function is an unused function of a template. It should fail to compile if you attempt to call the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With