Ideal return for functions changing their argument

There is an extensive discussion of ideal forwarding in boilerplate functions to ensure that lvalues ​​or rvalues ​​are effectively passed as parameters to other functions.

However, I cannot find a discussion of the ideal return or, what is the same, the perfect passage. (The related Perfect pass-through question does not completely resolve this.)

Consider the case of a function that changes the range and should return the changed range. We will need two separate functions to effectively address the cases of the lvalue and rvalue arguments:

// Given a reference to an lvalue range, return a reference to the same modified range. // (There is no allocation or move.) template<typename T> T& sortr(T& r) { std::sort(std::begin(r),std::end(r)); return r; } // Given an rvalue range, return the same range via (hopefully) a move construction. template<typename T> T sortr(T&& r) { std::sort(std::begin(r),std::end(r)); return std::move(r); } 

Of course, including both definitions lead to ambiguity errors, because the second also corresponds to lvalue links.

A motivating example (and using a test) is the following:

 #include <iostream> #include <vector> #include <algorithm> std::ostream& operator<<(std::ostream& os, const std::vector<int>& v) { os << "["; for (int i : v) { os << " " << i; } os << " ]\n"; return os; } int main() { std::vector<int> c1{3,4,2,1}; std::cerr << sortr(c1) << sortr(std::vector<int>({7,6,5,8})); } 

Is it possible to define both versions of sortr with one definition of template<typename T> ?

The problem is that declaring the return type as T&& will only produce the result (after matching with the pattern) in the T&& or T& return type, not T

Is it possible to determine the appropriate template function using metaprogramming by return type? Something like the following:

 template<typename T> auto sortr(T&& r) -> typename std::conditional<std::is_lvalue_reference<T>::value, T&, T>::type { std::sort(std::begin(r),std::end(r)); return std::forward<T>(r); } 

It seems to work, but I'm not sure if it is safe and wise. Any recommendations are appreciated.

+4
source share
1 answer

You need to use a universal link. In this case, you can only return T , but you need to change your template function as follows:

 template<typename T> T sortr(T && r) { std::sort(std::begin(r),std::end(r)); return std::forward<T>(r); } 

Full example:

 #include <iostream> #include <utility> #include <type_traits> #include <vector> #include <algorithm> template<typename T> T sortr(T && r) { std::sort(std::begin(r),std::end(r)); return std::forward<T>(r); } std::vector<int> foo() { return std::vector<int>(5,2); } void test( std::vector<int> & ) { std::cout<<"lvalue" << std::endl; } void test( std::vector<int> && ) { std::cout<<"rvalue" << std::endl; } int main() { std::vector<int> v(1,6); test( sortr( foo() ) ); test( sortr( v ) ); } 

If you don't know what a universal link is, take a look at this Scott Meyers talk .

+7
source

All Articles