Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 std::function and perfect forwarding

Why definition of std::function<>::operator() in the C++ standard is:

R operator()(ArgTypes...) const;

and not

R operator()(ArgTypes&&...) const;

?

One would think that to correctly forward parameters, we need the && and then use std::forward<ArgTypes>... in the function body when forwarding the call?

I partially reimplemented std::function to test this and I found out that if I use the &&, I get "cannot bind 'xxx' lvalue to 'xxx&&'" from g++ when I try later to pass parameters by value to operator(). I thought that I got enough grasp of the rvalue/forwarding concepts, but still I cannot grok this point. What am I missing?

like image 844
airman Avatar asked Jun 21 '12 09:06

airman


People also ask

What is perfect forwarding in C++?

What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.

What is the use of std :: forward?

std::forward If arg is an lvalue reference, the function returns arg without modifying its type. This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.

What is universal reference C++?

In a type declaration, “ && ” indicates either an rvalue reference or a universal reference – a reference that may resolve to either an lvalue reference or an rvalue reference. Universal references always have the form T&& for some deduced type T .


1 Answers

Perfect forwarding only works when the function itself (in this case operator()) is templated and the template arguments are deduced. For std::function, you get the operator() argument types from the template parameters of the class itself, which means they'll never be deduced from any arguments.

The whole trick behind perfect forwarding is the template argument deduction part, which, together with reference collapsing, is what perfect forwarding is.

I'll just conveniently link to my other answer about std::forward here, where I explain how perfect forwarding (and std::forward) works.

Note that std::function's operator() doesn't need perfect forwarding, since the user himself decides what the parameters should be. This is also the reason why you cannot just add && to operator(); take this example:

void foo(int){}

int main(){
  // assume 'std::function' uses 'ArgTypes&&...' in 'operator()'
  std::function<void(int)> f(foo);
  // 'f's 'operator()' will be instantiated as
  // 'void operator()(int&&)'
  // which will only accept rvalues
  int i = 5;
  f(i); // error
  f(5); // OK, '5' is an rvalue
}
like image 86
Xeo Avatar answered Sep 17 '22 15:09

Xeo