Why can't C++11 move a noncopyable functor to a std::function?
The short answer is that the C++11 specification requires your A
to be CopyConstructible
to be used with std::function
.
The long answer is this requirement exists because std::function
erases the type of your functor within the constructor. To do this, std::function
must access certain members of your functor via virtual functions. These include the call operator, the copy constructor and the destructor. And since these are accessed via a virtual call, they are "used" whether or not you actually use std::function
's copy constructor, destructor or call operator.
Can std::function be move-constructed from rvalue reference to a temporary functor object?
This object is really heavyweight, so it's marked as uncopyable, but it does have a move constructor.
If a functor is non-copyable, it does not meet the necessary requirements for being used with std::function
. Paragraph 20.8.11.2.1/7 of the C++11 Standard specifies:
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.f
shall beCallable
(20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor ofA
shall not throw exceptions.
How to store non-copyable std::function into a container?
std::ref
and std::cref
are meant to be used in this cases to avoid copying the object (see http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper).
Not sure that I got your question right, but this compiles for me:
#include <vector>
#include <functional>
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable &) = delete;
NonCopyable(NonCopyable &&) = default;
};
int main() {
std::vector<std::function<void()>> callbacks;
callbacks.emplace_back([] {});
NonCopyable tmp;
auto fun = std::bind([](const NonCopyable &) {}, std::cref(tmp));
callbacks.emplace_back(fun);
return 0;
}
EDIT: As mentioned in the comments, be careful about the life cycle of the referenced variable!
How can std::function be copied if it captures a unique_ptr?
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 to create an std::function from a move-capturing lambda expression?
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
Requires:
F
shall beCopyConstructible
.f
shall beCallable
for argument typesArgTypes
and return typeR
. The copy constructor and destructor of A shall not throw exceptions.§20.9.11.2.1 [func.wrap.func.con]
Note that operator =
is defined in terms of this constructor and swap
, so the same restrictions apply:
template<class F> function& operator=(F&& f);
Effects:
function(std::forward<F>(f)).swap(*this);
§20.9.11.2.1 [func.wrap.func.con]
So to answer your question: Yes, it is possible to construct a std::function
from a move-capturing lambda (since this only specifies how the lambda captures), but it is not possible to construct a std::function
from a move-only type (e.g. a move-capturing lambda which move-captures something that is not copy constructible).
Passing a lambda with moved capture to function
The error happens because your lambda has non-copyable captures, making the lambda itself not copyable. std::function
requires that the wrapped object be copy-constructible.
If you have control over call_func
, make it a template:
template<typename T>
void call_func(T&& func)
{
func();
}
int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
}
Following is my take on your idea in (2). Since std::function
requires the wrapped object to be copy-constructible, we can make our own function wrapper that does not have this restriction:
#include <algorithm>
#include <fstream>
#include <iterator>
#include <utility>
#include <memory>
#include <sstream>
#include <vector>
template<typename T>
void call_func(T&& func) {
func();
}
// All functors have a common base, so we will be able to store them in a single container.
struct baseFunctor {
virtual void operator()()=0;
};
// The actual functor is as simple as it gets.
template<typename T>
class functor : public baseFunctor {
T f;
public:
template<typename U>
functor(U&& f)
: f(std::forward<U>(f))
{}
void operator()() override {
f();
}
};
// In C++17 you don't need this: functor's default constructor can already infer T.
template<typename T>
auto makeNewFunctor(T&& v) {
return std::unique_ptr<baseFunctor>(new functor<T>{std::forward<T>(v)});
}
int main() {
// We need to store pointers instead of values, for the virtual function mechanism to behave correctly.
std::vector<std::unique_ptr<baseFunctor>> functors;
// Generate 10 functors writing to 10 different file streams
std::generate_n(std::back_inserter(functors), 10, [](){
static int i=0;
std::ostringstream oss{"test"};
oss << ++i << ".txt";
std::fstream fs{oss.str(), std::fstream::out};
return makeNewFunctor([fs = std::move(fs)] () mutable { fs.close(); });
});
// Execute the functors
for (auto& functor : functors) {
call_func(*functor);
}
}
Note that the overhead from the virtual call is unavoidable: Since you need functors with different behavior stored in the same container, you essentially need polymorphic behavior one way or the other. So you either implement this polymorphism by hand, or use virtual
. I prefer the latter.
Returning a lambda function with a move-capture
The problem is the std::function<int ()>
return type, which is attempting to make a copy of the lambda. This will fail because the copy constructor is implicitly deleted due to the presence of the std::unique_ptr
. Instead of storing the lambda in a std::function
object, use return type deduction, now the lambda will be moved.
auto makeLambda(unique_ptr<int> ptr) {
return [ ptr( move(ptr) ) ] () {
return *ptr;
};
}
Live demo
You should also probably change the argument type of makeLambda
to unique_ptr<int>&&
How to create a container of noncopyable elements
No, non-copyable elements can't be in C++ container classes.
According to the standard, 23.1 paragraph 3, "The type of objects stored in these components must met the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignable
types."
Related Topics
How to Enforce the 'Override' Keyword
How to Make an Image Resize to Scale in Qt
Why Implicit Conversion Is Harmful in C++
Implementing a Std::Vector Like Container Without Undefined Behavior
Qmake: How to Remove Compiler Flag for a Certain Project, Without Changing Qmake.Conf
Date/Time Conversion: String Representation to Time_T
Setting Pointer to Arbitrary Dimension Array
C/C++: Is This Undefined Behavior? (2D Arrays)
Memory Alignment:How to Use Alignof/Alignas
How to Avoid Errors While Using Crtp
How to Not Wait for a System() Command to Finish? (In C)
Initializing a Ublas Vector from a C Array
Print 2-D Array in Clockwise Expanding Spiral from Center
Undefined Reference to Mempcy@Glibc_2.14 When Compiling on Linux
Constraining the Existing Boost.Spirit Real_Parser (With a Policy)