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?
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.
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.
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.”
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.
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.
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.
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