I have an API which just enters a subscription into a vector of callbacks. The vector uses std::function which could be partially heap allocated, hence move operations on std::function make sense. Now I could implement this API in two ways:
Take by value
Take by rvalue reference
The chances that the user just puts in a lambda (rvalue ref) are really big in this case. If I take by value it will move-construct the new function object in my function body, but I actually just want to forward it to the vector element construct. This would therefore be an unnecessary inefficiency.
On the other hand, if I take by rvalue ref, the user is forced to provide an rvalue. But what if he wants to distribute the function object to multiple sinks? Right, he just has to make the copy the object himself before the call.
Now: Why not always aim for the second design, which is, taking rvalue references and let the caller make the copy? It doesn't imply any penalties on the user, he just hast to remember to copy the object first IF he wants to move the object elsewhere as well, but other than that the user stays in full controll of how the API should behave and will never have the performance penalty associated with move-constructing the object.
Demo
#include <functional>
#include <cstdio>
#include <vector>
struct entity
{
auto on_change(std::function<void()>&& cb)
{
callbacks_.push_back(std::move(cb));
}
auto print_all() {
std::for_each(callbacks_.cbegin(), callbacks_.cend(), [](const auto cb){ cb(); });
}
std::vector<std::function<void()>> callbacks_;
};
auto foo() -> void {
printf("Hello from function!\n");
}
int main()
{
entity e,d;
// Normal case
e.on_change([](){ printf("Hello from lambda!\n"); });
// Special case
auto fn = std::function<void()>([]{ printf("Hello from function that gets distributed multiple times!\n"); });
// these two don't work
// e.on_change(fn);
// d.on_change(fn);
// But why not like this?
e.on_change(std::function<void()>(fn));
d.on_change(std::move(fn));
e.print_all();
d.print_all();
}
Output:
Hello from lambda!
Hello from function that gets distributed multiple times!
Hello from function that gets distributed multiple times!
Note: This other question deals with the differentiation of rvalue vs lvalue which is not what this question is about.
Related
Recently I find myself often in the situation of having a single function that takes some object as a parameter. The function will have to copy that object.
However the parameter for that function may also quite frequently be a temporary and thus I want to also provide an overload of that function that takes an rvalue reference instead a const reference.
Both overloads tend to only differ in that they have different types of references as argument types. Other than that they are functionally equivalent.
For instance consider this toy example:
void foo(const MyObject &obj) {
globalVec.push_back(obj); // Makes copy
}
void foo(MyObject &&obj) {
globalVec.push_back(std::move(obj)); // Moves
}
Now I was wondering whether there is a way to avoid this code-duplication by e.g. implementing one function in terms of the other.
For instance I was thinking of implementing the copy-version in terms of the move-one like this:
void foo(const MyObject &obj) {
MyObj copy = obj;
foo(std::move(copy));
}
void foo(MyObject &&obj) {
globalVec.push_back(std::move(obj)); // Moves
}
However this still does not seem ideal since now there is a copy AND a move operation happening when calling the const ref overload instead of a single copy operation that was required before.
Furthermore, if the object does not provide a move-constructor, then this would effectively copy the object twice (afaik) which defeats the whole purpose of providing these overloads in the first place (avoiding copies where possible).
I'm sure one could hack something together using macros and the preprocessor but I would very much like to avoid involving the preprocessor in this (for readability purposes).
Therefore my question reads: Is there a possibility to achieve what I want (effectively only implementing the functionality once and then implement the second overload in terms of the first one)?
If possible I would like to avoid using templates instead.
My opinion is that understanding (truly) how std::move and std::forward work, together with what their similarities and their differences are is the key point to solve your doubts, so I suggest that you read my answer to What's the difference between std::move and std::forward, where I give a very good explanation of the two.
In
void foo(MyObject &&obj) {
globalVec.push_back(obj); // Moves (no, it doesn't!)
}
there's no move. obj is the name of a variable, and the overload of push_back which will be called is not the one which will steal reasources out of its argument.
You would have to write
void foo(MyObject&& obj) {
globalVec.push_back(std::move(obj)); // Moves
}
if you want to make the move possible, because std::move(obj) says look, I know this obj here is a local variable, but I guarantee you that I don't need it later, so you can treat it as a temporary: steal its guts if you need.
As regards the code duplication you see in
void foo(const MyObject &obj) {
globalVec.push_back(obj); // Makes copy
}
void foo(MyObject&& /*rvalue reference -> std::move it */ obj) {
globalVec.push_back(std::move(obj)); // Moves (corrected)
}
what allows you to avoid it is std::forward, which you would use like this:
template<typename T>
void foo(T&& /* universal/forwarding reference -> std::forward it */ obj) {
globalVec.push_back(std::forward<T>(obj)); // moves conditionally
}
As regards the error messages of templates, be aware that there are ways to make things easier. for instance, you could use static_asserts at the beginning of the function to enfornce that T is a specific type. That would certainly make the errors more understandable. For instance:
#include <type_traits>
#include <vector>
std::vector<int> globalVec{1,2,3};
template<typename T>
void foo(T&& obj) {
static_assert(std::is_same_v<int, std::decay_t<T>>,
"\n\n*****\nNot an int, aaarg\n*****\n\n");
globalVec.push_back(std::forward<T>(obj));
}
int main() {
int x;
foo(x);
foo(3);
foo('c'); // errors at compile time with nice message
}
Then there's SFINAE, which is harder and I guess beyond the scope of this question and answer.
My suggestion
Don't be scared of templates and SFINAE! They do pay off :)
There's a beautiful library that leverages template metaprogramming and SFINAE heavily and successfully, but this is really off-topic :D
A simple solution is:
void foo(MyObject obj) {
globalVec.push_back(std::move(obj));
}
If caller passes an lvalue, then there is a copy (into the parameter) and a move (into the vector). If caller passes an rvalue, then there are two moves (one into parameter and another into vector). This can potentially be slightly less optimal compared to the two overloads because of the extra move (slightly compensated by the lack of indirection) but in cases where moves are cheap, this is often a decent compromise.
Another solution for templates is std::forward explored in depth in Enlico's answer.
If you cannot have a template and the potential cost of a move is too expensive, then you just have to be satisfied with some extra boilerplate of having two overloads.
If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
Take the following code for example ... when do the various moves happen? Is it thread safe if myFunction is called on one thread and testLambda is executed on another thread?
class MyClass {
private:
// Only accessed on thread B
std::vector<int> myStuff;
// Called from thread A with new data
void myFunction(const std::vector<int>&& theirStuff) {
// Stored to be called on thread B
auto testLambda = [this, _theirStuff{ std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
// ... store lambda
}
// Elsewhere on thread A
void someOtherFunction() {
std::vector<int> newStuff = { 1, 2, .... n };
gGlobalMyClass->myFunction(std::move(newStuff));
}
If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
If you had written what I believe you intended to write, then the answer would be: both. Currently, the answer is: neither. You have a lambda capture _theirStuff { std::move(theirStuff) }. This basically declares a member of the closure type, which will be initialized when the closure object is created as if it were
auto _theirStuff { std::move(theirStuff) };
You also have
myStuff = std::move(_theirStuff);
in the lambda body.
However, your parameter theirStuff is actually an rvalue reference to a const std::vector<int>. Thus, _theirStuff { std::move(theirStuff) } is not actually going to perform a move, because a const std::vector cannot be moved from. Most likely, you wanted to write std::vector<int>&& theirStuff instead. Furthermore, as pointed out by #JVApen in the comments below, your lambda is not mutable. Therefore, _theirStuff will actually be const as well, and, thus, also cannot be moved from. Consequently, your code above, despite all the std::move, will actually make a copy of the vector every time. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
}
You would be moving theirStuff into _theirStuff when the closure object is created. And you would be copying _theirStuff into myStuff when the lambda is called. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() mutable {
myStuff = std::move(_theirStuff);
};
}
Then you would be moving theirStuff into _theirStuff when the closure object is created. And you would be moving _theirStuff into myStuff when the lambda is called. Note that, as a consequence, your lambda then cannot really be called twice. I mean, it can, but it will only really work once since _theirStuff will be empty after the first time the lambda is called…
Also, note that above description is only valid for the particular combination of types in your example. There is no general definition of what it actually means to move an object. What it means to move an object is entirely up to the particular type of the object. It may not even mean anything. std::move itself does not really do anything. All it does is cast the given expression to an rvalue reference. If you then initialize another object from the result of std::move, or assign the result to an object, overload resolution will pick a move constructor or move assignment operator—if one exists—instead of the normal copy constructor or copy assignment operator. It is then up to the implementation of the move constructor/move assignment operator of the respective type to actually perform a move, i.e., do whatever it is that's supposed to be done for the particular type in case of initialization or assignment from an rvalue. So, in a way, what you do when you apply std::move is that you advertise the respective object as "this may be moved from". Whether or not it actually will be moved from (and, if so, what that actually means) is up to the implementation. In the particular case of std::vector, the move constructor/move assignment operator, by definition, guarantee that not only the contents of the original vector will be taken over from the original object, but also that the original object will be empty afterwards. In many other cases, it may be undefined behavior to do anything with an object that was moved from (except, maybe, destroy it; that one can be pretty much taken for granted as a type that doesn't at least allow that would be pretty much useless; typically, you will at least be able to assign a new value to an object that was moved from, but even that is not guaranteed in general). You always have to check for the particular type at hand what condition an object is guaranteed to be in after having been moved from…
This question relates to the DelayedCaller in this answer.
The DelayedCaller binds a function pointer and its arguments and works like a charm as long as the the arguments aren't pointers to things shorter-lived than the DelayedCaller's execution (think string.c_str() of a local variable as argument).
To circumvent this issue I created a storage for problematic arguments handled by a template function scanning the arguments.
What I need now is kinda the opposite: I want the member function to be called on a different object of the same type, by evaluating the the address of the pointer given to the DelayedCaller as an argument.
I currently see two ways to go about this:
std::placeholders: Instead of providing the object when creating the DelayedCaller it is provided with the call method.
a wrapper for the object pointer that dereferences twice (overloading ->).
I favor 2. over 1. (I don't want to have to provide an argument whenever using call()), but there might other options I am not even considering.
Example:
#include <iostream>
#include <string>
#include <functional>
#include <memory>
class MyClass
{
float myFloat;
public:
MyClass(float f):myFloat(f){}
void myFunc(int arg1)
{
std::cout << arg1 << ", " << myFloat << '\n';
}
};
class MyContainer
{
public:
MyClass* object;
};
// DelayedCaller implementation
class DelayedCaller
{
public:
template <typename TFunction, typename... TArgs>
static std::shared_ptr<DelayedCaller> setup(TFunction&& a_func,
TArgs&&... a_args)
{
return std::shared_ptr<DelayedCaller>(new DelayedCaller(
std::bind(std::forward<TFunction>(a_func),
std::forward<TArgs>(a_args)...)));
}
void call() const { func_(); }
private:
using func_type = std::function<void()>;
DelayedCaller(func_type&& a_ft) : func_(std::forward<func_type>(a_ft)) {}
func_type func_;
};
int main()
{
MyContainer container;
MyClass* c1 = new MyClass(45.6);
container.object = c1;
// the next line is the critical one. Instead of myFunc being called
// on the current value of container.object, it should be called on
// the one container.object is holding when caller is called.
auto caller(DelayedCaller::setup(&MyClass::myFunc, container.object, 123));
caller->call();
MyClass* c2 = new MyClass(22.8);
container.object = c2;
caller->call();
delete c1;
delete c2;
return 0;
}
std::reference_wrapper might help, use:
auto caller(DelayedCaller::setup(&MyClass::myFunc, std::ref(container.object), 123));
Demo
How about leaving "binding" to C++ compiler with usage of lambda expression?
auto caller(DelayedCaller::setup([&container] { container.object->myFunc(123);}));
Gives the output:
123, 45.6
123, 22.8
PS. This lambda is directly convertible into std::function<void(void)>, thus DelayedCaller constructor could be public and created without setup function if wanted. If you can edit its implementation of course.
Your really need to understand what std::bind does.
When using std::bind, it will copy(or move respectively if they are respectively r-value, but as a reminder, whether they are moved depend on callable objects and each arguments feed into std::bind respectively)
Next, I'll discuss the situation when there's only one argument, but the rule of it applies to situation where there're more than one arguments respectively.
Both the callable object(which could be pointers to functions or functors) and the arguments into the object of std::function returned by std::bind, so it will never occur the case you asked.
If the argument is not a pointer, or reference, you can just feed the (temporary) objects into sts::bind and the c++ standard promises that it will only be copied or moved, not by reference unless you use std::ref or std::cref to wrap that object.
But if you feed std::unique_ptr or other things that wraps a pointer(or reference as shown in above), you need to consider them as a pointer(reference), not an object(although copy/move choices still occurs)
If the argument is a pointer, then the pointer will be copied into it.
Will, in this case, if the pointer points to a local variable, bingo, then you have one bug to track.
Actually the code you write will work well if (the destructor of the object does not do any damage to the object in the memory, in other word, the object exists after it is destructed, like POD and etc) && (the memory contains the object is not reused by any other objects, when sth like this (another function is called and its local variables occupies the memory of that object and write something to it) happens it is reused).
If the pointer points to sth you allocated on heap and it is not deallocated until your call of the DelayedCaller::cal is done, then there's nothing wrong with you code.
If the pointer is a constant pointer that points to a literal, then it will be all fine.
The case when the argument is a reference is roughly the same as the case when it's a pointer.
Reference: http://en.cppreference.com/w/cpp/utility/functional/bind
The compiler tells me I'm trying to access a deleted function (i.e. the copy constructor of a lambda expression). But I don't see where.
std::vector<std::function<void()>> tasks;
std::packaged_task<int()> task{ [] { return 1; } };
tasks.emplace_back(
[ t = std::move(task) ] () mutable { t(); });
(code is also here)
(I'm trying to find out why they use shared_ptr<task> in https://www.slideshare.net/GlobalLogicUkraine/c11-multithreading-futures).
On Gcc and MSVC I get the same error - I fear I'm doing something wrong...
error: use of deleted function
'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
Why can't I emplace this std::function onto the vector?
From cppreference:
F must meet the requirements of Callable and CopyConstructible
Where F is the function type used to construct the std::function. However, std::packaged_task is not copy constructible. Thus in the capture list, t is not copy constructible, and is a non-static member of the lambda, making the implicit copy constructor for the lambda deleted.
Short answer: Lambdas and std::packaged_task are not std::functions.
Long answer, you cannot move a std::packaged_task into a std::function
Here's what I'm offering as a solution:
std::vector<std::packaged_task<int()>> tasks;
std::packaged_task<int()> task{ [] () mutable { return 1; } };
tasks.emplace_back( std::move(task) );
If you actually need a std::function, and not just any callable, you'll have to bind a lambda into a std::function
The constructor of std::function requires the passed function object to be CopyConstructible, but std::packaged_task<F> is not (for any F). std::function performs type erasure where the dynamic type is not visible in the static type. Consider e.g.:
int invoke(std::function<int()> f) { return f(); }
int main()
{
std::packaged_task<int()> p{/*etc*/};
auto l = [] { return 5; };
std::function<int()> f( /* either p or l */ );
std::cout << invoke(f) << '\n';
}
The call to invoke requires copying f (pass by value). However, f is copyable if it has been made from l, but not copyable if it has been made from p, and this has nothing to do with the static type of f. There are basically three approaches to this problem:
Forbid copying of std::function at compile time.
Allow copying std::function at compile time, but throw a run time error if the contained type is not copyable.
Allow copying std::function at compile time, and require any function object that you put into it to be copyable.
Approach #1 is very restrictive on how functions may be stored, passed and shared, and basically forbids common use cases in favor of the uncommon case of using a non-copyable function object.
Approach #2 is problematic because users would need to be educated that copying a std::function may fail in some cases and use great diligence when writing their code. Also, if the design requires sharing functions they may need to be wrapped in a std::shared_ptr. And if they need to be copied and may be stateful, it becomes even worse.
No matter how you view approach #3, it is the one that was standardized. But in light of the above-mentioned problems, it is also easily to defend.
As a matter of fact, I have written a unique_function class template that uses approach #1 for my current project because for us the use case of storing non-copyable asynchronous task objects is quite common, and copying or sharing such a task isn't necessary.
I'd like to use lambda functions to asynchronously call a method on a reference counted object:
void RunAsync(const std::function<void()>& f) { /* ... */ }
SmartPtr<T> objPtr = ...
RunAsync([objPtr] { objPtr->Method(); });
Creating the lambda expression obviously creates a copy but I now have the problem that converting the lambda expression to a std::function object also creates a bunch of copies of my smart pointer and each copy increases the reference count.
The following code should demonstrate this behavior:
#include <functional>
struct C {
C() {}
C(const C& c) { ++s_copies; }
void CallMe() const {}
static int s_copies;
};
int C::s_copies = 0;
void Apply(const std::function<void()>& fct) { fct(); }
int main() {
C c;
std::function<void()> f0 = [c] { c.CallMe(); };
Apply(f0);
// s_copies = 4
}
While the amount of references goes back to normal afterwards, I'd like to prevent too many referencing operations for performance reasons. I'm not sure where all these copy operations come from.
Is there any way to achieve this with less copies of my smart pointer object?
Update: Compiler is Visual Studio 2010.
std::function probably won't be as fast as a custom functor until compilers implement some serious special treatment of the simple cases.
But the reference-counting problem is symptomatic of copying when move is appropriate. As others have noted in the comments, MSVC doesn't properly implement move. The usage you've described requires only moving, not copying, so the reference count should never be touched.
If you can, try compiling with GCC and see if the issue goes away.
Converting to a std::function should only make a move of the lambda. If this isn't what's done, then there's arguably a bug in the implementation or specification of std::function. In addition, in your above code, I can only see two copies of the original c, one to create the lambda and another to create the std::function from it. I don't see where the extra copy is coming from.