Rody Oldenhuis
Rody Oldenhuis

Reputation: 38032

Implicit casting obscures overloaded operator. How to resolve?

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:

My problems:

  1. 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.

  2. 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

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

Answers (3)

Andriy
Andriy

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

Henrik
Henrik

Reputation: 23324

Derived::operator* hides Base::operator*. Try inserting using Base::operator* in class Derived to make operator*(double) available in Derived.

Upvotes: 1

Jason
Jason

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

Related Questions