Suppose I want swap
that works on rvalues, and don't want to write 4 versions for all combinations of rvalue/lvalue references (rvalue/rvalue version is kinda pointless but it doesn't hurt). I came up with this:
template <typename A, typename B>
struct is_same_no_ref
: std::is_same<
typename std::remove_reference<A>::type,
typename std::remove_reference<B>::type
>
{};
template <typename A, typename B,
typename = typename std::enable_if<is_same_no_ref<A, B>::value>::type
>
inline void my_swap(A&& a, B&& b) {
typename std::remove_reference<A>::type t = std::move(a);
a = std::move(b);
b = std::move(t);
}
which seems to work as expected. Is this okay? Or am I missing something important that will make me suffer later?
Best Answer
While I see no inherent concept flaw in your implementation, I would make three suggestions.
(note: I'll refer here to an implementation of the concept of swaping rvalues as
swap_rvalues
)Exclude const types from the template deduction
Assuming
change the enable condition from
std::enable_if<std::is_same_no_ref<A, B>::value>
into the following.Without excluding const types from the template deduction, passing const variables to
swap_rvalues
, as in the following,induces the compiler to flag errors regarding internal implementation details, what is not very user-friendly.
Move the enable condition to the return type declaration
Instead of
declare it like the following
Even though highly improbable, the explicit definition of the third template parameter of
swap_rvalues
is possible, effectively overriding the enable condition. This may allow code to compile which shouldn't and nastiness could follow. This is completely avoided using the return type declaration for the enable condition.Consider the following example.
It compiles, even though it clearly shouldn't!
A
andB
are not even related, they just happen to be constructable given a reference of the other.Reuse code [aka KISS]
Since the rvalues are already given a name, simply forward call
std::swap
, instead of providing a brand new implementation ofswap_rvalues
.Why reinventing the wheel†?
std::swap
already provides the intended behavior once rvalues are given a name, so why not reusing it?Conclusion
The final implementation of
swap_rvalues
‡ would look like follows.Footnotes
† "To reinvent the wheel is to duplicate a basic method that has already previously been created or optimized by others."
‡
swap_rvalues
in fact would better be calledswap
in a real scenario.