Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

min and perfect forwarding

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);
}
like image 582
fredoverflow Avatar asked Jun 22 '10 17:06

fredoverflow


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

How does STD forward work?

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 reference collapsing?

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.


1 Answers

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!

like image 85
Jerry Coffin Avatar answered Oct 04 '22 19:10

Jerry Coffin