You can do this using a 4 overloads:
Base Base::doSomething(Base& other) && {
// *this is an rvalue and other is an lvalue
}
Base Base::doSomething(Base&& other) && {
// *this and other are rvalues
}
Base Base::doSomething(Base& other) & {
// *this and other are lvalues
}
Base Base::doSomething(Base&& other) & {
// *this is an lvalue and other is an rvalue
}
If you want to be able to accept const Base
's then you can make these templates and use SFINAE to make sure the decayed template type is a Base
like
template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, Base>, bool> = true>
Base Base::doSomething(T& other) && {
// *this is an rvalue and other is an lvalue
}
Base Base::doSomething(Base&& other) && {
// *this and other are rvalues
}
template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, Base>, bool> = true>
Base Base::doSomething(T& other) & {
// *this and other are lvalues
}
Base Base::doSomething(Base&& other) & {
// *this is an lvalue and other is an rvalue
}
Another way to do this is with just two overloads and forwarding references. Then you can check if the type of the parameter is an lvalue reference to determine if you have an lvalue or rvalue. This works when you use forwarding references since the type of the template parameter is deduced to be T&
instead of T
if it was an rvalue. That would give you code like
template<typename A, typename B, typename C>
Base Base::doSomething(A&& other1, B&& other2, C&& other3) &
{
// this is always an lvalue in this function
if (std::is_lvalue_reference_v<A>)
// other1 is an lvalue
else
// other1 is a rvalue
if (std::is_lvalue_reference_v<B>)
// other2 is an lvalue
else
// other2 is a rvalue
if (std::is_lvalue_reference_v<C>)
// other3 is an lvalue
else
// other3 is a rvalue
}
template<typename A, typename B, typename C>
Base Base::doSomething(A&& other1, B&& other2, C&& other3) &&
{
// this is always an rvalue in this function
if (std::is_lvalue_reference_v<A>)
// other1 is an lvalue
else
// other1 is a rvalue
if (std::is_lvalue_reference_v<B>)
// other2 is an lvalue
else
// other2 is a rvalue
if (std::is_lvalue_reference_v<C>)
// other3 is an lvalue
else
// other3 is a rvalue
}
And then if you need to you can constrain the template by adding
std::enable_if_t<std::is_same_v<std::decay_t<A>, type_for_other1>,
std::is_same_v<std::decay_t<B>, type_for_other2>,
std::is_same_v<std::decay_t<B>, type_for_other3>, bool> = true
To the template parameters to constrain the template to the types you want for the parameters.
to each of