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!
Why can't I convert a lambda with a non-copyable class parameter to a std::function?
The diagnostic is super misleading. While it is obvious that mutex is not copyable, there is no copying of mutex requested in the provided snippet!
The immediate cause of issue is the fact that std::function<>
templated constructor (one which would be called when std::function
is created from the lambda) is SFINAE-restricted on object being Callable
with types of arguments provided.
Callable
is not satisfied, since the calling expression tries to pass mutex by value, and mutex is not copyable.
As a result, compiler can't find any suitable constructor to construct the std::function
object and issues diagnostic observed.
It also seems the inability to construct an std::function
like that is somewhat unfortunate, since you would be able to call a normal function or lambda with temporary-constructed object like
f(t{});
due to guaranteed copy elision. While it makes no sense to do so for std::mutex
, it could be meaningful for other non-copyable, non-movable classes.
Passing a non-static method or std::functionvoid(...) as a void (*) argument
As stated, this is not possible since there is no way to meaningfully construct a plain function pointer from a (non static) method, a closure, or a std::function
object.
Roughly speaking, each of the constructs above are logically formed by two parts: some fixed code (a pointer to a fixed, statically known function), and variable data (the object at hand, or the captures for a closure). We can not simply throw away the second part.
That being said, I would recommend to check if the library will call the dispatcher
passing a user-defined pointer for its void *
argument. It's somewhat common to see C style library functions like
void register_callback(void (*dispatcher)(int,int,void *), void *user_data);
This is a trick to allow to simulate closures in C, passing the two "parts" of the closure separately.
If that's the case, instead of
// not working, just for example
std::function<void(int, int)> f;
register_callback(f);
You can write
// Make sure f will live long enough.
// I'm using new to stress the point, but you can use anything
// else provided that the pointer will be valid when f is
// called.
std::function<void(int, int)> *pf = new ...;
register_callback(call_the_function, pf);
provided you have defined
// fixed function, independent from the std::function object
void call_the_function(int x, int y, void *p) {
std::function<void(int,int)> *pf =
(std::function<void(int,int)>*) p;
(*pf)(x,y);
}
If you used new
to keep the pointer valid long enough, remember to delete
it when the callback is no longer needed.
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.
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).
type deduction for std::function argument types with auto adds const
The problem is that generic lambdas (auto param) are equivalent to a callable object whose operator()
is templated. This means that the actual type of the lambda argument is not contained in the lambda, and only deduced when the lambda is invoked.
However in your case, by having specific std::function arguments, you force a conversion to a concrete type before the lambda is invoked, so there is no way to deduce the auto type from anything. There is no SFINAE in a non-template context.
With no specific argument type, both your call
are valid overloads. Actually any std::function that can match an [](auto&)
is valid. Now the only rule is probably that the most cv-qualified overload wins. You can try with a volatile float&
and you will see it will still choose that. Once it choose this overload, the compilation will fail when trying to invoke.
Passing a Lambda Expression to std::function in C++
The problem
The problem you have can be reduced to this:
#include <functional>
template <typename ...Args>
void foo(std::function<bool(Args...)>) { }
int main()
{
foo([](int, int) { return true; });
}
which will fail to compile. The reason for this is that the type deduction for the template arguments for std::function
fails.
You expect a std::function
of some sort to be passed as an argument. Since no std::function
object (of whatever exact instantiation) is passed in, the compiler tries to construct a std::function
and deduce the template parameter types by calling the constructor of std::function
. Here starts the problem. The matching constructor in this case would be:
template <typename F>
function(F f);
You will notice that the constructor itself is also templated. The compiler could successfully deduce F
as a lambda, but since F
is a template parameter of the constructor, the template parameter of the class itself std::function
cannot be deduced.
Furthermore, to quote cppreference on that constructor:
[...] This constructor does not participate in overload resolution
unless f is Callable for argument types Args... and return type R. [...]
This means that the existance of this constructor is based on whether F
can be called with the class template arguments Args...
, but since those are not explicitly defined and cannot be deduced, this constructor won't be available anyway.
The solution
Since you only use that std::function
inside exportSelectedData
, simply make it a template parameter alltogether (ditching the std::function
part):
template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;
You should also change Args_T&&
to Args_T const&
since you don't simply forward those arguments but reuse them inside a loop.
Edit
Regarding your follow-up question in the edit: think about what you're doing.
First you declare a lambda:
auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };
Now think about the signature of that lambda. You return a boolean so the return type is bool
(so far so good). You take u32MaxCmdDuration
in the capture-clause and you take one argument singleElement
. Lets remove all the extra qualifiers and look at the signature:
bool(CombinedDictElement) // take a CombinedDictElement and return a bool
Next, we take a look at the call of exportSelectedData
:
exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
You pass in u32MutexTimeout
and lambda
which is perfectly fine, the lambda is captued by compareFn
. The third argument is u32MaxCmdDuration
which is captured by ...compareArgs
in your template. Now lets take a look at where you actually invoke the lambda inside exportSelectedData
:
if (compareFn(singleElement, compareArgs...)) // ...
What signature do you expect compareFn
to have here? If we expand the ...compareArgs
pack (again, removing the extra qualifiers for simplicity) the signature looks like this:
bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool
Here is the lambda signature again:
bool(CombinedDictElement)
Do you spot the problem? The lambda captures u32MaxCmdDuration
as a state capture while exportSelectedData
expects it as an parameter (since you passed it into exportSelectedData
as an additional argument). The signatures differ from each other, obviously, so we have to change one of them to match the other. This is fairly easy in your case, either
change your lambda to take
u32MaxCmdDuration
as a parameter:auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)
or
remove
u32MaxCmdDuration
as an additional argument to yourexportSelectedData
call:exportSelectedData(u32MutexTimeout, lambda);
Related Topics
Const Member and Assignment Operator. How to Avoid the Undefined Behavior
How to Embed Node.Js Interpreter into C/C++
Segmentation Fault Before Main() When Using Glut, and Std::String
Random Numbers for Multiple Threads
Msvc Equivalent of _Attribute_ ((Warn_Unused_Result))
Why Is Std::For_Each a Non-Modifying Sequence Operation
Why Does Std::Map Not Have a Const Accessor
Why Does My Program Run Way Faster When I Enable Profiling
How to Share Memory Between Linux Program and Windows Program Running Through Wine (Same Computer)
What Is The Linux Equivalent Of: Multibytetowidechar & Widechartomultibyte
Ubuntu System Monitor and Valgrind to Discover Memory Leaks in C++ Applications
Are End+1 Iterators for Std::String Allowed
How Visitor Pattern Avoid Downcasting
Std::Chrono::System_Clock::Now() Considering The Os Configured Time Zone
Advantages of Using Arrays Instead of Std::Vector
Remove Reference in Decltype (Return T Instead of T& Where T& Is the Decltype)