Во-первых, я не понимаю, почему operator + вообще изменяет аргументы (разве это не типичная реализация неизменяемого двоичного дерева), поэтому не будет никакой разницы между r-значением и l-значением. Но давайте предположим, что у поддеревьев есть указатель на родителя или что-то в этом роде.
Из примера использования, который вы показали, похоже, что существует неявное преобразование из double в дерево. В этом случае ваши случаи «приведения и пересылки» не нужны, компилятор найдет преобразование, определенное пользователем.
Разве перегрузки, не связанные с перемещением, не приводят к созданию нового экземпляра для перехода в новое дерево? Если это так, я думаю, что вы можете написать три из оставшихся четырех дел в качестве экспедиторов.
tree operator +(tree&& a, tree&& b); // core case
tree operator +(tree a, tree b) { return std::move(a) + std::move(b); }
tree operator +(tree a, tree&& b) { return std::move(a) + std::move(b); }
tree operator +(tree&& a, tree b) { return std::move(a) + std::move(b); }
Конечно, вы можете использовать макрос для генерации трех (или семи) версий переадресации каждого оператора.
РЕДАКТИРОВАТЬ: если эти вызовы неоднозначны или разрешают рекурсию, как насчет:
tree add_core(tree&& a, tree&& b);
tree operator +(tree&& a, tree&& b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree a, tree b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree a, tree&& b) { return add_core(std::move(a), std::move(b)); }
tree operator +(tree&& a, tree b) { return add_core(std::move(a), std::move(b)); }
РЕДАКТИРОВАТЬ: repro оператора не использовать неявные преобразования:
#include <iostream>
template<typename T>
class tree;
template<typename T> tree<T> add(tree<T> a, tree<T> b)
{
std::cout << "added!" << std::endl << std::endl;
return tree<T>();
}
template<typename T> tree<T> operator +(tree<T> a, tree<T> b) { return add(a, b); }
template<typename T>
class tree
{
public:
tree() { }
tree(const tree& t) { std::cout << "copy!" << std::endl; }
tree(double val) { std::cout << "double" << std::endl; }
friend tree operator +<T>(tree a, tree b);
};
int main()
{
tree<double>(1.0) + 2.0;
return 0;
}
И версия без шаблонов, где работает неявное преобразование:
#include <iostream>
class tree
{
public:
tree() { }
tree(const tree& t) { std::cout << "copy!" << std::endl; }
tree(double val) { std::cout << "double" << std::endl; }
friend tree operator +(tree a, tree b);
};
tree add(tree a, tree b)
{
std::cout << "added!" << std::endl << std::endl;
return tree();
}
tree operator +(tree a, tree b) { return add(a, b); }
int main()
{
tree(1.0) + 2.0;
return 0;
}