Apparently std::function::operator=(F &&f)
is required to behave exactly as std::function(std::forward<F>(f)).swap(*this);
.
Unless I'm missing something, this definition causes some superfluous moving:
#include <functional>
#include <iostream>
struct A
{
A() {std::cout << "A()\n";}
A(const A &) {std::cout << "A(const A &)\n";}
A(A &&) {std::cout << "A(A &&)\n";}
A &operator=(const A &) {std::cout << "A &operator=(const A &)\n"; return *this;}
A &operator=(A &&) {std::cout << "A &operator=(A &&)\n"; return *this;}
~A() {std::cout << "~A()\n";}
void operator()() const {}
};
int main()
{
std::function<void()> f;
f = A{};
}
Prints:
A() // Created by `A{}` A(A &&) // Moved into temporary `std::function`, but what's the point? A(A &&) // Moved into `f` ~A() ~A() ~A()
(tested on GCC 7.2 and Clang 3.8)
Question: Why can't we eliminate one move, by copying/moving (depending on value category) directly into LHS storage?
Edit: I'm not asking why the move is not optimized away, but rather why it's made in the first place.
std::function can hold more than function pointers, namely functors. Live example on Ideone. As the example shows, you also don't need the exact same signature, as long as they are compatible (i.e., the parameter type of std::function can be passed to the contained function / functor).
If there is any possibility you are storing a copy of the std::function , pass by value. Otherwise, either way is roughly equivalent: the only downside to by-value is if you are taking the same bulky std::function and having one sub method after another use it.
They are not the same at all. std::function is a complex, heavy, stateful, near-magic type that can hold any sort of callable entity, while a function pointer is really just a simple pointer. If you can get away with it, you should prefer either naked function pointers or auto - bind / auto -lambda types.
If it is small, like 3-5 CPU instructions then yes std::function will make it slower, because std::function is not inlined into outer calling code. You should use only lambda and pass lambda as template parameter to other functions, lambdas are inlined into calling code.
When constructing the temporary of std::function
, the called constructor is
template< class F >
function( F f );
which is pass-by-value, so the first move is in fact a move into the parameter of this constructor, while the second move is the move into the temporary. Basically, typical implementation of std::function
stores a pointer to its callable target, so swapping the pointer is sufficient for its swap
function, which will not involve in any copy/move of its callable target.
Because the type of the stored callable target in LHS may be different from that of RHS, you cannot directly perform a copy/move.
swap()
involved in?This is called copy-and-swap idiom, which behaves as an assignment with strong exception safety.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With