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
doubleand toDerived, to enable its subclasses to be (implicitly) cast toDerivedtype. Call one of these base classesOtherDerived. -
Instances of
Derivedcan then be constructed either by a call to one of the the named constructors inDerived, or by a pass throughOtherDerivedas 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.0is 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*thisisBaseClass<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;
}
In case #2 of your
maincode, your problem seems to stem from the fact you would like yourBase<Derived>class to create aDerivedobject inside ofBase<Derived>::operator*=when the constructor forDerivedis private … you’ll have to make theBase<Derived>class a friend ofDerivedin order for the constructor to be called.For case #3 of your
maincode, I would define aoperator*for yourOtherBaseorOtherDerivedclass to prevent the implicit casting todouble.