Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perfect Forwarding in C++03

Tags:

If you have this function

template<typename T> f(T&); 

And then try to call it with, let's say an rvalue like

f(1); 

Why isn't T just be deduced to be const int, making the argument a const int& and thus bindable to an rvalue?

like image 827
Puppy Avatar asked Aug 28 '10 18:08

Puppy


People also ask

What is perfect forwarding?

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 point of std :: forward?

std::forward Returns an rvalue reference to arg if arg is not an lvalue reference. If arg is an lvalue reference, the function returns arg without modifying its type.

What is STD forward?

std::forward has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called “perfect forwarding.”

What is a forwarding reference in C++?

When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.


2 Answers

This is mentioned as a potential solution in the document I linked in the recent C++0x forwarding question.

It would work fairly well, but it breaks existing code. Consider (straight from the document):

template<class A1> void f(A1 & a1) {     std::cout << 1 << std::endl; }  void f(long const &) {     std::cout << 2 << std::endl; }  int main() {     f(5);              // prints 2 under the current rules, 1 after the change     int const n(5);     f(n);              // 1 in both cases } 

Or

// helper function in a header  template<class T> void something(T & t) // #1 {     t.something(); }  // source  #include <vector>  void something(bool) // #2 { }  int main() {     std::vector<bool> v(5);      // resolves to #2 under the current rules, #1 after the change     something(v[0]); } 

This also fails to forward the value category (lvalue or rvalue), which isn't much of a problem in C++03. But since this fix could only be done during C++0x, we'd effectively shutting ourselves out from rvalue references when forwarding (a bad thing). We should strive for a better solution.

like image 137
GManNickG Avatar answered Nov 16 '22 12:11

GManNickG


It is, but only if you declare f to take T const &.

template <typename T> void f(T &); template <typename T> void g(T const &);  void x() { f(1); }  // error: invalid initialization of non-const reference void y() { g(1); }  // no error 

And if you declare both f(T &) and f(T const &), it'll choose the const-qualified one:

template <typename T> void f(T &); template <typename T> void f(T const &);  void x() { f(1); } // no error, calls f(T const &) 

Now maybe you're saying “in the first example, why does it generate a temporary of type int for the call to f when it could have generated a temporary of type const int and made the code compile?” The best answer I have for you is that that would be inconsistent with the overload resolution behavior when the argument isn't an integer constant.

like image 41
zwol Avatar answered Nov 16 '22 13:11

zwol