Vote count:
0
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 two different compilers:
Visual C++ (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++, 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 all arguments, no matter if they are passed by value or by reference, like below:
template <class Ret, class... Args>
class function<Ret(Args...)> {
// ...
Ret operator()(Args... args) { // by value, like in the signature
return impl->call(std::forward<Args>(args)...);
}
function_impl<Ret(Args...)>* impl;
};
template <class Ret, class... Args>
struct function_impl<Ret(Args...)> { // interface
virtual Ret call(Args&&... args) = 0; // rvalues or collaped lvalues
};
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.
So my question is, why does GCC perform additional copy/move operation for arguments passed by value? 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.
Why doesn't GCC's std::function internally use rvalue references for arguments passed by value?
Aucun commentaire:
Enregistrer un commentaire