The min algorithm is normally expressed like this:
template <typename T>
const T& min(const T& x, const T& y)
{
return y < x ? y : x;
}
However, this does not allow constructs of the form min(a, b) = 0
. You can achieve that with an additional overload:
template <typename T>
T& min(T& x, T& y)
{
return y < x ? y : x;
}
What I would like to do is unify these two overloads via perfect forwarding:
template <typename T>
T&& min(T&& x, T&& y)
{
return y < x ? std::forward<T>(y) : std::forward<T>(x);
}
However, g++ 4.5.0 spits out a warning for min(2, 4)
that I return a reference to a temporary. Did I do something wrong?
Okay, I get it. The problem is with the conditional operator. In my first solution, if I call min(2, 4)
the conditional operator sees an xvalue and thus moves from the forwarded x
to produce a temporary object. Of course it would be dangerous to return that by reference! If I forward the whole expression instead of x
and y
seperately, the compiler does not complain anymore:
template <typename T>
T&& min(T&& x, T&& y)
{
return std::forward<T>(y < x ? y : x);
}
Okay, I got rid of the references for arithmetic types :)
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type
min(T x, T y)
{
return y < x ? y : x;
}
template <typename T>
typename std::enable_if<!std::is_arithmetic<T>::value, T&&>::type
min(T&& x, T&& y)
{
return std::forward<T>(y < x ? y : x);
}
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.
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.
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.”
Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references.
It looks to me like you're trying to oversimplify the problem. Unfortunately, getting it entirely correct is decidedly non-trivial. If you haven't read N2199, now would be a good time to do so. Rvalue references continue to evolve, so its reference implementation of min and max probably isn't exactly right anymore, but it should at least be a pretty decent starting point. Warning: the reference implementation is a lot more complex than you're going to like!
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