There are a lot of similar questions here on SO (e.g.: How to get different overloads for rvalue and lvalue references with a template-deduced type?), but not exactly this one. In particular, no questions are concered with value returning functions. Furthermore, I am not sure (euphemestically spoken) whether I understood the answers correctly.
I want to provide two versions of a function, which either return a new object or change the passed object (and return the changed object -- for consistency). For simplicity I present my code using a unique like algorithm.
User code
auto vec = std::vector<int>{1,3,5,2,4};
auto vec1 = tt::unique( vec );
// does not change vec and returns new vector
// vec1 should be {1,2,3,4,5}
// vec should be {1,3,5,2,4}
auto vec2 = tt::unique( std::move(vec) );
// does change vec and returns new vector vec2
// vec2 should be {1,2,3,4,5}
// vec should be empty
Implementation:
namespace tt {
template< typename T > T unique( T && vec ) {
std::sort( vec.begin(), vec.end() );
vec.erase(
std::unique( vec.begin(), vec.end()),
vec.end()
);
return std::move( vec );
}
template< typename T > T unique( T & vec ) {
return unique( T(vec) );
}
}
To avoid code duplication the lvalue reference version calls the rvalue reference version.
My main questions:
template< typename T > T sort( T & array ) {
T array_ = array;
std::sort( array_.begin(), array_.end() );
return array_;
}
Is this efficient/correct code?Extra questions:
Why is this overload never used and why does is compile at all, given that c is const.
template< typename T >
T unique( const T && c ) {
std::cout << "Rvalue reference unique wrapper!\n";
return unique( T(c) );
}
Actually I would like to have const in the lvalue reference signature, i.e. something like
template<typename T> T unique( const T & vec );
. But, as I read, since the first is a forwarding reference, this does not work.
Thus, what do I have to do to get const correctness here (My question is about C++14, but I am also interrested in C++17 answers).
Through the magic of reference collapsing...
template<class T>
[[nodiscard]] std::decay_t<T> unique(T&& t) {
std::decay_t<T> vec=std::forward<T>(t);
std::sort( vec.begin(), vec.end() );
vec.erase(
std::unique( vec.begin(), vec.end() ),
vec.end()
);
return vec;
}
works for both cases. DRY.
Note that view types, like std span, have issues.