Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloaded operators & inheritance & templates (A formidable combination)

Greetings all.

I am writing some code using the Boost Units library and have run into a problem.

I have managed to abstract the problem from Boost code so you won't be looking through reams of boost template meta programming. Though I'm sure if you have experience with that it could help. Here is the reproduction:

class Base{};
class Derived : public Base
{
public:
  Derived(){}
  Derived(const Base &){}
};

class Q {};
class U
{
public:
  template< typename Y >
  Q operator * (Y)
  {
    Q r;
    return r;
  }
};

Base operator * (U, const Base &)
{
  Base r;
  return r;
}

int main(int argc, char **argv)
{
  Base myBase;
  U myU;
  Base myOtherBase = myU * myBase;
  Derived myDerived;
  Derived myOtherDerived =  myU * myDerived;
  return 0;
}

So the problem (specifically) is as follows: myU * myBase uses operator * (U, const Base &) and returns type of Base, all good so far. Whereas myU * myDerived insists on using generalised U::operator * (Y) and hence returns a Q, no good because I wanted a Base again.

Now, all classes other than Base and Derived are boost library classes so I cannot modify the members of U. How do I "beat" U::operator * (Y) for overload/template deduction/instantiation, in this case, in an elegant and "solved once and for ever" manner.

I am using MSVC++ 2008 in case it is relevant to anyone.

Edit: Added a possible (quite likely) solution in answers

like image 409
EdF Avatar asked May 14 '11 03:05

EdF


3 Answers

Using the following should fix your problem

Base myOtherDerived =  myU * (Base&)myDerived;
// or
Base myOtherDerived =  myU * static_cast<Base&>(myDerived);

instead of

Derived myOtherDerived =  myU * myDerived;

This is not what we could call a "clean solution" though. I'm trying to find a better way to do it.

like image 52
Valkea Avatar answered Sep 28 '22 17:09

Valkea


First, the problem: The const Base& parameter of the operator* will always be a worse fit than the template parameter's exact fit because of conversion from Derived to Base.
Next, the solution: Provide overloaded operator* for every derived class. :(

like image 30
Xeo Avatar answered Sep 28 '22 19:09

Xeo


If you have control over the ordering of the operators, you could define your operator* in the opposite direction, i.e.,

Base operator* (const Base& lhs, U rhs)
{
    Base r;
    return r;
}

Now if you tried

Derived myOtherDerived =  myDerived * myU;

it would not fit the template in class U, getting you around the issue of the template function in U overriding your own operator* function.

like image 24
Jason Avatar answered Sep 28 '22 18:09

Jason