First, consider the following code:
#include <iostream>
#include <functional>
struct Noisy
{
Noisy() { std::cout << "Noisy()" << std::endl; }
Noisy(const Noisy&) { std::cout << "Noisy(const Noisy&)" << std::endl; }
Noisy(Noisy&&) { std::cout << "Noisy(Noisy&&)" << std::endl; }
~Noisy() { std::cout << "~Noisy()" << std::endl; }
};
void foo(Noisy n)
{
std::cout << "foo(Noisy)" << std::endl;
}
int main()
{
Noisy n;
std::function<void(Noisy)> f = foo;
f(n);
}
and its output in different compilers:
Visual C++ (see live)
Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()
Clang (libc++) (see live)
Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()
GCC 4.9.0 (see live)
Noisy()
Noisy(const Noisy&)
Noisy(Noisy&&)
Noisy(Noisy&&)
foo(Noisy)
~Noisy()
~Noisy()
~Noisy()
~Noisy()
That is, GCC performs one more move/copy operation compared to Visual C++ (and Clang+libc++), that, let's agree, is not efficient in all cases (like for std::array<double, 1000>
parameter).
To my understanding, std::function
needs to make a virtual call to some internal wrapper that holds actual function object (in my case foo
). As such, using forwarding references and perfect forwarding is not possible (since virtual member functions cannot be templated).
However, I can imagine that the implementation could std::forward
internally all arguments, no matter if they are passed by value or by reference, like below:
// interface for callable objects with given signature
template <class Ret, class... Args>
struct function_impl<Ret(Args...)> {
virtual Ret call(Args&&... args) = 0; // rvalues or collaped lvalues
};
// clever function container
template <class Ret, class... Args>
struct function<Ret(Args...)> {
// ...
Ret operator()(Args... args) { // by value, like in the signature
return impl->call(std::forward<Args>(args)...); // but forward them, why not?
}
function_impl<Ret(Args...)>* impl;
};
// wrapper for raw function pointers
template <class Ret, class... Args>
struct function_wrapper<Ret(Args...)> : function_impl<Ret(Args...)> {
// ...
Ret (*f)(Args...);
virtual Ret call(Args&&... args) override { // see && next to Args!
return f(std::forward<Args>(args)...);
}
};
because arguments passed-by-value will just turn into rvalue references (fine, why not?), rvalue references will collapse and remain rvalue references, as well as lvalue references will collapse and remain lvalue references (see this proposal live). This avoids copies/moves between any number of internal helpers/delegates.
So my question is, why does GCC perform additional copy/move operation for arguments passed by value, while Visual C++ (or Clang+libc++) does not (as it seems unnecessary)? I would expect the best possible performance from STL's design/implementation.
Please note that using rvalue references in std::function
signature, like std::function<void(Noisy&&)>
, is not a solution for me.
Please note that I am not asking for a workaround. I perceive neither of the possible workarounds as correct.
a) Use const lvalue references !
Why not? Because now when I invoke f
with rvalue:
std::function<void(const Noisy&)> f = foo;
f(Noisy{});
it inhibits move operation of Noisy
temporary and forces copy.
b) Then use non-const rvalue references !
Why not? Because now when I invoke f
with lvalue:
Noisy n;
std::function<void(Noisy&&)> f = foo;
f(n);
it does not compile at all.
_M_invoker
, hence the extra copy. – Bluestonestd::function
is based on ourstd::tr1::function
which was written many years before rvalue-references existed. I've optimised other call wrappers such asstd::bind
,std::thread
andstd::async
to use perfect forwarding butfunction
might still be suboptimal. I'll take a look. – Gobo