Using std::move to pass in a temp lambda, or to “pull” out a temp parameter and what is the difference?

  • A+
Category:Languages

I have the following (contrived) code where I have a printer class with a single print function in it and a working class that processes a string and then calls a callback function to the print function:

#include <functional> #include <iostream>  using callback_fn = std::function<bool(std::string)>;  class printer { public:        bool print(std::string data)     {         std::cout << data << std::endl;         return true;     } };  class worker { public:        callback_fn m_callback;     void set_callback(callback_fn callback)     {         m_callback = std::move(callback);  // <-- 1. callback is a temp, so what does std::move do here?     }     void process_data(std::string data)     {         if (!m_callback(data)) { /* do error handling */ }     } };  int main() {     printer p;     worker w;      w.set_callback( std::move([&](std::string s){ return p.print(s); }) ); // <-- 2. what does std::move do here?     w.process_data("hello world2"); } 

Note: I have std:: move() called twice... now this works (surprisingly to me) but I have both only to show what I am trying. My questions are:

  1. Should I use std::move() in the set_callback() function to "pull" out the temp, and if I use this is there really a copy or does std:: move() mean that its not really a copy?
  2. Should I use std:: move() to pass in the lambda... and is that even correct.
  3. I guess I don't understand why this code works with two std:: moves()... which implies that I still don't understand what std:: move() is doing - so if someone can elighten me on what is going on here that would be great!
  4. I know that I can pass by value, but my goal is to move the temp so that I don't have a copy of it (perfect forwarding?)

My example can be seen here in wandbox: https://wandbox.org/permlink/rJDudtg602Ybhnzi

UPDATE The reason for me trying to use std::move was to avoid copying the lambda around. (I think that is called forwarding/perfect-forwarding)...but I think I am making a hash of it!

 


I guess I don't understand why this code works with two std::moves... which implies that I still don't understand what std::move is doing

std::move is there to make it explicit in cases you are intending to take any expression (such as an lvalue) and make an rvalue out of it. This arises typically when you need to allow an lvalue to be passed to the function overloads that accept rvalue reference as a parameter, such as move constructors and move assignment operators. In your example cases in the current snippet there is no use for it.

Should I use std::move to pass in the lambda

Seemingly no, you have no reason of doing that in the snippet. First of all, you are calling move() on a what is unconditionally already an rvalue. Further, syntactically, set_callback() is receiving its std::function<bool(std::string)> argument by value, of which your lambda is initializing an instance just fine at present.

Should I use std::move in the set_callback() function

It isn't clear what you are gaining by using the move version of the assignment operator onto the m_callback member variable, instead of the regular assignment. It won't cause any undefined behavior though, as you are not trying to use the argument after moving it. Also, since C++11 the callback parameter in set_callback() will be move constructed for rvalues such as your temporary, and copy constructed for an lvalue, such as if you'd call it like this:

auto func = [&](std::string s){ return p.print(s); }; w.set_callback(func); 

What you need to be considering is whether inside the method moving is better then copying in your case. Moving involves its own implementation of the move assignment for the relevant type. I'm not just saying QOI here, but consider that when moving you need to release whatever resource m_callback was holding up to that point, and for the scenario of moving from a constructed instance (as we've covered that callback has been either copy constructed or move-constructed from its argument), that's adding to the cost this construction already had. Not sure that such a moving overhead applies in your case, but still your lambda is a not obviously expensive to copy as it is. That said, opting for two overloads, one taking a const callback_fn& callback and copy-assigning inside and one taking a callback_fn&& callback and move-assigning inside would allow to mitigate this potential issue altogether. As in either case you don't construct anything and overall you're not necessarily releasing old resources as an overhead.

I know that I can pass by value, but my goal was to move the temp so that I don't have a copy of it (perfect forwarding?)

In the context of type deduction (template or auto), a T&& is a forwarding reference, not an rvalue reference. As such you only have to write the function once (template function, no overloads), and relying internally on std::forward (equivalent to static_cast<T&&>) will make sure that in any use-case, the above described path for using the two overloads is preserved in terms of the cost being a copy-assignment for an lvalue call and a move-assignment for an rvalue call:

template<class T> void set_callback(T&& callback) {     m_callback = std::forward<T>(callback); } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: