27

I am trying to write some subclasses of classes in a big library. I am getting an "ambiguous base" error. Here is a compilable example of the problem:

#include <iostream>

// I can't change these because they are in the library:
class InteractorStyle {};
class InteractorStyleCamera : public InteractorStyle {};
class InteractorStyleImage : public InteractorStyle {};

// These are my subclasses (so I can change them):
class PointSelector : public InteractorStyle {};
class PointSelector2D : public InteractorStyleCamera, public PointSelector
{
  // This function has to exist exactly like this (a requirement of the library):
  static PointSelector2D* SafeDownCast(InteractorStyle *o)
  {
    return static_cast<PointSelector2D *>(o);
  } 
};


int main()
{

  return 0;
}

The error is

error: ‘InteractorStyle’ is an ambiguous base of ‘PointSelector2D’.

Is there anything I can do in this case?

Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
David Doria
  • 9,873
  • 17
  • 85
  • 147

4 Answers4

20

Your problem is that Interactor style is inherited twice - once by PointSelector2D and once by InteractorStyleCamera. This means that you have 2 versions of every member of it contained within your class.

Check out:

How can I avoid the Diamond of Death when using multiple inheritance?

And try virtual inheritance.

Community
  • 1
  • 1
John Humphreys
  • 37,047
  • 37
  • 155
  • 255
  • I think that the problem is that the right way to add in the virtual inheritance requires the OP to modify code that he can't modify. It's too bad, because the unmodifiable code should definitely be using virtual inheritance. – templatetypedef Aug 25 '11 at 22:29
  • Ah, I figured he had the library source to play with - if he doesn't he might be boned :p Might have to go around the problem and try and get the same results with containment if that's the case. – John Humphreys Aug 25 '11 at 22:31
  • Yep, templatetypedef is right - I was posting here to see if there were any other options. – David Doria Aug 25 '11 at 22:32
  • w00te - is that solution to keep an object of PointSelector inside the PointSelector2D class? That seems so awkward, doesn't it? – David Doria Aug 25 '11 at 22:35
  • It's very awkward, I agree. Hopefully you'll find a way to inherit and get it to compile, but if you can't containment is the only other way to maintain the existing functionality and extend it. Thats why things like the STL have you make container-adaptors (i.e. queue) by containing existing STL containers like deque. I completely agree its not something you want to do unless you run out of options though. – John Humphreys Aug 25 '11 at 22:39
5

You can superficially "fix" it by using a two-step cast. For example

static_cast<PointSelector2D *>(static_cast<InteractorStyleCamera *>(o));

Of course, you have to keep in mind that this "fixes" the syntax, but doesn't fix the underlying structural problem. Your PointSelector2D has two InteractorStyle base subobjects inside. Depending from which InteractorStyle base subobjects you start, the upcast path is different. And it is very important to take the correct path. What I wrote above is for InteractorStyle inside InteractorStyleCamera. For the other base the proper upcast would be

static_cast<PointSelector2D *>(static_cast<PointSelector *>(o));

If you are just given an InteractorStyle * pointer with no extra information about which base it is pointing to, then you can't possibly solve your problem with static_cast. There's no way to know which upcast path to take. Taking the wrong one will produce a totally meaningless result.

As it has been noted already, dynamic_cast can help in this situation, but it has additional requirements (polymorphic starting type). Your types are not polymorphic (at least in your quoted example), so dynamic_cast will not accept them for upcasts.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

I believe you can fix this by using a dynamic_cast instead of a static_cast. The dynamic_cast can look at the object at runtime to determine which of the two InteractorStyle base classes is being pointed at, and from there can adjust the pointer down to the appropriate type.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • You should be able to freely cast pointer types right? I can cast a `vector*` to an `int*` so why would `static_cast` be the problem? – Seth Carnegie Aug 25 '11 at 22:23
  • Unfortunately this function is in a much larger macro that is necessary for the library I am using to understand that this is indeed one of its objects. Is there anything else I can do that doesn't involve changing that function? – David Doria Aug 25 '11 at 22:24
  • @Seth Carnegie- `static_cast` only works if there's an unambiguous downcast to perform from the base type to the derived type. In this case, there are two base objects of type `InteractorStyle` - one from `PointSelector` and one from `InteractorStyleCamera` - so the compiler can't know at compile-time which of the two objects is being pointed at, and so it can't adjust the pointer to point to the base of the correct object. Using a C-style cast here is dangerous because it wouldn't adjust the pointer. Only `dynamic_cast` can use runtime information to determine how to adjust the pointer. – templatetypedef Aug 25 '11 at 22:27
  • @template ah thanks, I thought `static_cast` was basically a C-style cast. – Seth Carnegie Aug 25 '11 at 22:28
0

You can get your code to compile (I got your example to compile at least) by adding InteractorStyle to the classes that PointSelector2D inherits from. Whether this is a long term solution is not known by me.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • You mean change it to: class PointSelector2D : public InteractorStyleCamera, public PointSelector, public InteractorStyle ? that still doesn't compile for me. – David Doria Aug 25 '11 at 22:32
  • @David weird, try putting `InteractorStyle` first in the list. Other than that, I guess it just must be my compiler. Which one are you using? Also are you compiling your example code or the real project? – Seth Carnegie Aug 25 '11 at 22:35
  • nope, even first in the list it doesn't work. I'm using g++ 4.5 and compiling the example code I posted here. – David Doria Aug 25 '11 at 22:37