Reputation: 38032
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 is private
.
Another baseclass implements cast operators to double
and to Derived
, to enable its subclasses to be (implicitly) cast to Derived
type. Call one of these base classes OtherDerived
.
Instances of Derived
can then be constructed either by a call to one of the the named constructors in Derived
, or by a pass through OtherDerived
as in Derived D = OtherDerived(5.0);
My problems:
Defining Derived D = OtherDerived(5.0) * 2.0;
seems to cast OtherDerived(5.0)
to double instead of Derived
, so that the multiplication by 2.0
is simply a product of doubles, and NOT the output of operator*
in Derived
's base class.
The definition of operator*=
in the templated base class cannot return a reference to *this
, since the type of *this
is BaseClass<T>
while the desired type of the reference is just T&
.
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;
, or Derived D = OtherDerived(...) * Derived::someName(2.0);
, or Derived 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;
}
Upvotes: 3
Views: 230
Reputation: 8604
For problem #1, you can implement a standalone multiplication operator:
Derived operator*(const OtherDerived& op1, double op2)
{
return (Derived)val1*Derived::someName(val2);
}
In addition, both conversion operators in OtherBase
should be const
.
Upvotes: 1
Reputation: 23324
Derived::operator*
hides Base::operator*
. Try inserting using Base::operator*
in class Derived
to make operator*(double)
available in Derived
.
Upvotes: 1
Reputation: 32510
In case #2 of your main
code, your problem seems to stem from the fact you would like your Base<Derived>
class to create a Derived
object inside of Base<Derived>::operator*=
when the constructor for Derived
is private ... you'll have to make the Base<Derived>
class a friend of Derived
in order for the constructor to be called.
For case #3 of your main
code, I would define a operator*
for your OtherBase
or OtherDerived
class to prevent the implicit casting to double
.
Upvotes: 1