I have multiple classes that should support addition. For each of them, the operator += is implemented as T& operator+=(const T& rhs)
. I also want to add the operator +, but this implementation will use += and will therefore be similiar in all classes. My idea was to implement a template class using the Curiously Recurring Template Pattern that provides an implementation. The result is code like this:
template<class T>
class Sumable {
private:
Sumable() = default;
public:
Sumable(const Sumable<T>&) = delete;
Sumable(Sumable<T>&&) = delete;
T operator+(const T&) const&;
T operator+(const T&) && ;
T operator+(T&&) const&;
T operator+(T&&) && ;
friend T;
};
template<class T>
inline T Sumable<T>::operator+(const T & rhs) const&
{
T result(*static_cast<const T*>(this));
return std::move(result += rhs);
}
template<class T>
inline T Sumable<T>::operator+(const T & rhs) &&
{
return std::move<T&>(*static_cast<T*>(this) += rhs);
}
template<class T>
inline T Sumable<T>::operator+(T && rhs) const&
{
return std::move(rhs += *this);
}
template<class T>
inline T Sumable<T>::operator+(T && rhs) &&
{
return std::move(rhs += *this);
}
Classes that support addition would extend from Sumable<T>
like for example class Vector : public Sumable<Vector>
The operator is overloaded four times to prevent the creation of unnecessary temporary objects. The idea is described here https://stackoverflow.com/questions/6006527/overloading-on-r-value-references-and-code-duplication, but I return T by value to prevent the problems described in this question. I also follow this idea https://stackoverflow.com/questions/11224838/prevent-user-from-deriving-from-incorrect-crtp-base/11241079 to prevent incorrect use of the class.
In a simple test, this worked like expected. Are there any edge cases in which this design can cause problems? And is it a good idea to use this design in production code?
Edit The code I used to test this, compiles under MSVS 2017:
#include "stdafx.h"
#include <iostream>
#include "Sumable.h"
using namespace std;
struct Test : public Sumable<Test>{
int data;
Test(int data) : data(data) {};
Test(const Test& that) : data(that.data)
{
cout << "copy" << endl;
};
Test(Test&& that) : data(that.data)
{
cout << "move" << endl;
};
Test& operator+=(const Test& that) {
this->data += that.data;
return *this;
}
};
int main()
{
Test a{ 1 };
Test b{ 2 };
Test c{ 3 };
cout << (a + b + c + a + b + c).data << endl;
return 0;
}