I have a fairly complicated construct. I'm also fairly certain I've been staring at it for way too long, and all those trees now obscure my view of the forest. So I'll give you the full complication of my construct, even though I suspect only a small portion of it is actually relevant.
Now, my construct in words:
A templated baseclass, derived from its own baseclass, implements several operator overloads.
Classes subclassed from the templated baseclass (using their own name as template parameter) will have access to all the operators. This construct ensures that operators can only operate on equal types, as per this question.
A single specific subclass implements a few specialized versions of the operators. Call this class
Derived
. This subclass also uses named constructors, so its main constructor isprivate
.Another baseclass implements cast operators to
double
and toDerived
, to enable its subclasses to be (implicitly) cast toDerived
type. Call one of these base classesOtherDerived
.Instances of
Derived
can then be constructed either by a call to one of the the named constructors inDerived
, or by a pass throughOtherDerived
as inDerived D = OtherDerived(5.0);
My problems:
Defining
Derived D = OtherDerived(5.0) * 2.0;
seems to castOtherDerived(5.0)
to double instead ofDerived
, so that the multiplication by2.0
is simply a product of doubles, and NOT the output ofoperator*
inDerived
's base class.The definition of
operator*=
in the templated base class cannot return a reference to*this
, since the type of*this
isBaseClass<T>
while the desired type of the reference is justT&
.
How to resolve these issues elegantly?
I consider the second problem minor, since I can easily work around it by not returning a reference at all. Still, it would be nice to have one. Most important to me though is: how do I enable people to write Derived D = OtherDerived(...) * 2.0
or some similar, simple form?
Demanding people to write
Derived D = (Derived)OtherDerived(...) * 2.0;
, orDerived D = OtherDerived(...) * Derived::someName(2.0);
, orDerived D = OtherDerived(...).operator*(2.0);
etc. seems rather strange and unnecessary...
NB: preferably, the operator double()
remains available :)
Here is an MWE:
#include <iostream>
#include <cmath>
class SuperBase
{
public:
virtual ~SuperBase(){}
SuperBase() : value(0.0) {}
protected:
double value;
SuperBase(double value) : value(value) {}
};
template <class T>
class Base : public SuperBase
{
public:
virtual ~Base(){}
Base() : SuperBase() {}
T& operator*=(double F) { value *= F; return *this; }
T operator* (double F) const { return T(value*F); }
double operator*(const T& U) const {
return value*U.value;
}
protected:
double value;
Base(double value) : SuperBase(value) {}
};
class Derived final : public Base<Derived>
{
public:
~Derived(){}
Derived() : Base<Derived>(){}
static Derived someName(double value) {
return Derived(value);
}
Derived operator*(const Derived& D) const {
return Derived(value/D.value);
}
private:
Derived(double value) : Base<Derived>(value) {}
};
class OtherBase
{
public:
virtual ~OtherBase(){}
operator double () { return value; }
operator Derived() { return Derived::someName(value); }
protected:
double value;
};
class OtherDerived final : public OtherBase
{
public:
~OtherDerived(){}
OtherDerived(double value){
this->value = std::sqrt(value);
}
};
int main(int argc, char *argv[])
{
Derived a = OtherDerived(1.05); // Compiles fine
Derived b = OtherDerived(1.05); // Compiles fine
b *= 2.0; // Error: cannot cast Base<Derived>
// to Derived
Derived c = OtherDerived(1.05)*2.0; // Error: casts the
// OtherDerived to double,
// so Derived(double) gets
// called, which is private
return 0;
}