0

I'm working on making some changes to a piece of code, and have a doubt in understanding the behavior of std::move in below case:

struct Timer {
   Timer (boost::asio::io_service& ios) : timer_{ios} {}
   boost::asio::steady_timer timer_;
};

struct TimerContext {
   void *ctxt_;
};

class A {
   std::function<void(Timer *, const TimerContext&)>    callback_;
   boost::asio::io_service                              ios_;
};

A's constructor:

A::A (std::function<void(Timer *, const TimerContext&)> cb) : callback_{std::move(cb)}
{
}

The callback function definition:

void
customCallback (Timer *timer, const TimerContext& ctxt) {
...
}

In main(),

for (int i = 0; i < 5; ++i) {

    A *a = new A{customCallback};

}

My doubt is for this line: A::A (std::function<void(Timer *, const TimerContext&)> cb) : callback_{std::move(cb)}

Class A is getting instantiated in a loop, and the same customCallback function is getting moved to the custom constructor for each new object.

Will the first std::move not make callback function unusable for next call? As I understood, if you use std::move(t), then t cannot be used again in that scope.

I'm confused what is happening internally here for all 5 calls to std::move(cb) on the same function in new A. Is this the right way to do how it is implemented?

void
  • 338
  • 5
  • 19
  • It's worth noting that since your std::function wraps a plain function pointer it contains no allocated memory pointers, and therefore the std::move will not be any more efficient than a regular copy operation – Patrick Parker Sep 19 '21 at 07:44

2 Answers2

1

Look carefully at A's constructor:

A::A (std::function<void(Timer *, const TimerContext&)> cb)

The function, cb is being passed by value. That means a copy of the function has already occurred from when it was invoked via new:

A *a = new A{customCallback};

The std::move in the constructor initializer list exists to avoid a redundant copy into the member variable. The original function defined by the caller who invoked new A remains unmoved. This is preferred because copying a std::function variable can be expensive. (sizeof(cb) - might be way bigger than you expected).

An alternative implementation: The function could have been passed as a const reference and allow the copy to occur in the constructor itself:

A::A (const std::function<void(Timer *, const TimerContext&)>& cb) : callback_{cb}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • does the alternative implementation suggested by you have any benefit over the one used in my question? – void Sep 29 '21 at 16:15
1

Your constructor A::A (std::function<void(Timer *, const TimerContext&)> cb) takes its parameter by value, i.e. customCallback is first copied into cb, then moved from that copy into callback_.

So every time the constructor is called, the copy is moved from, not the original customCallback object.

Also, you should avoid using raw calls to new and prefer smart pointers instead to avoid memory leaks, but that was not the question at hand here :-)

Matthias Grün
  • 1,466
  • 1
  • 7
  • 12