Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return forwarding reference parameters - Best practice

In the following scenario

template <class T>
? f(T&& a, T&& b)
{
    return a > b ? a : b; 
}

what would the optimum return type be ? My thoughts so far are :

  1. Return by r value refs, perfectly forwarding the function arguments :

    template <class T>
    decltype(auto) f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
  2. Move construct the return value :

    template <class T>
    auto f(T&& a, T&& b)
    {
        return a > b ? forward<T>(a) : forward<T>(b); 
    }
    
  3. Try fo find a way to enable (N)RVOs (even though I think since I want to use the function arguments that's impossible)

Is there a problem with these solutions ? Is there a better one ? What's the best practice ?

like image 714
Lorah Attkins Avatar asked Mar 17 '16 07:03

Lorah Attkins


1 Answers

For option 1, you need to use two template parameters to enable forwarding to happen when the two arguments have different value categories. It should be

template <typename T, typename U>
decltype(auto) f(T&& a, U&& b)
{
    return a > b ? std::forward<T>(a) : std::forward<U>(b);
}

What actually gets returned here is pretty tricky to work out. First you need to know the value categories of a and b, and what T and U get deduced as. Then whatever pairing you choose goes through the very complicated rules for the ternary operator. Finally, the output of that goes through the decltype rules to give you the actual return type of the function.

I did actually sit down and work it all out once, with a little help from SO. To the best of my recollection, it goes like this: the result will be a mutable lvalue reference if and only if a and b are lvalue references of compatible types; the result will be a const lvalue reference if one of a and b is a const lvalue reference and the other is any sort of reference; otherwise f will return a new value.

In other words, it does the right thing for any given pair of arguments -- probably because somebody sat down and wrote the rules exactly so that it would do the right thing in all cases.

Option 2 is doing to do exactly the same as option 1, except that the decltype rules don't come in to play, and the function will always return a new value -- plain auto never deduces to a reference.

I can't think of an option 3 that would work RVO-wise, since as you say you're using the arguments.

All in all, I think (1) is the answer you're looking for.

like image 200
Tristan Brindle Avatar answered Nov 10 '22 01:11

Tristan Brindle