views:

2823

answers:

6

I have a diamond multiple inheritance scenario like this:

    A
  /   \
 B     C
  \   /
    D

The common parent, A, defines a virtual function fn().
Is it possible for both B and C to define fn()?
If it is, then the next question is - can D access both B and C's fn() without disambiguation? I'm assuming there is some syntax for this..
And is it possible for D to do that without knowing specifically who are B and C? B and C can be replaces by some other classes and I want the code in D to be generic.

What I'm trying to do is to have D somehow enumerate all of the instances of fn() it has in its ancestry. Is this possible in some other means that virtual functions?

+1  A: 

There are already several questions that deal with this. Seems like we're running out of questions to ask. Maybe the search box should be bigger than the Ask Question button.

See

Scottie T
I couldn't find a question that answers this specific issue. can you?
shoosh
It's not because it's about multiple inheritance that you can guess it was already addressed in other posts. He's asked 'What I'm trying to do is to have D somehow enumerate all of the instances of fn() it has in its ancestry. Is this possible in some other means that virtual functions?'. Though I think it was a somewhat naive question, none of the questions you've linked here talk about such a thing. I think he was pretty specific and unique in his questioning. -1.
n2liquid
+2  A: 

First question, yes, B and C can define fn() as a virtual function. Second, D can of course access B::fn() and C::fn() by using the scope operator :: Third question: D must at least know B and C, since you have to define them on the inheritance list. You can use templates to let the types of B and C open:

class A
{
public:
   virtual ~A() {}
   virtual void fn() = 0;
};

class B: public A
{
public:
   virtual ~B() {}
   virtual void fn(){ std::cout << "B::fn()" << std::endl; }
};

class C: public A
{
public:
   virtual ~C() {}
   virtual void fn(){ std::cout << "C::fn()" << std::endl; }
};

template <typename TypeB, typename TypeC>
class D: public TypeB, public TypeC
{
public:
   void Do()
   {
      static_cast<TypeB*>(this)->fn();
      static_cast<TypeC*>(this)->fn();
   }
};

typedef D<B, C> DInst;

DInst d;
d.Do();

About the wish to automatically enumerate all fn() functions of all classes that D inherits from: I'm not sure if that is possible without resorting to MPL. At least you can extend my example above with versions that deal with 3 and more template parameters, but I guess there is an upper (internal compiler-)limit of number of class template parameters.

vividos
I was going to post the same. There is a slight mistake as D must inherit from both B and C and that is not in the code above. Another syntax (simpler than the cast) would be: TypeB::fn() and TypeA::fn() that seem more natural.
David Rodríguez - dribeas
I'm not sure if a TypeB::fn() call does the right thing with respect to the virtual function call. With the static cast you're sure you have an object of type B. I guess I have to try that out. Thanks for the note about the correction!
vividos
This is probably the closest solution to what I need. Unfortunatly in my case the number of classes in the inheritance (B,C) is also variable. guess this will have to wait for C++0x's variable template arguments.
shoosh
does that really work? i believe it is not valid because now both C and B override A's fn. the compiler does not know what version is the "final overrider" thus emitting an error message.
Johannes Schaub - litb
litb, where do you expect an error message? For the compiler there's no ambiguity, since I'm forcing it to TypeB or TypeC. A plain fn() call would error, though.
vividos
imagine you have this code: A *a = some_d_object; a->fn(); what version of fn will be called?
Johannes Schaub - litb
it forbids that situation already at the time you define D, because in objects of it, there is no final overrider of fn that overrides A::fn the most dominant.
Johannes Schaub - litb
@litb is correct. fn() is virtual and can (1) be overridden by D, and (2) if it is not overridden by D, it is overridden by B or C (but not both). Thus both your calls in Do would do the same thing, and the same thing as casting to A or D.
strager
-1. @vividos: Assuming you change D to override fn() (needed to get the code to compile), static_cast<TypeB*>(this)->fn() will still call the most-derived version (i.e. D's version) of fn(), because virtual functions ignore the static type. You need to say TypeB::fn() to do what you want.
j_random_hacker
j_random_hacker: The original question poster doesn't want to override fn() in D, he wants to enumerate and call all fn() functions of the classes in his inheritance list. And you don't have to override fn() in D to get it compile. Try it out.
vividos
-1. Litb is right: not overriding fn() in D generates a compiler error (I just tried). Your code doesn't compile due to ambiguous inheritance of 'void A::fn()'.
Luc Touraille
I completed the code with classes A, B and C to show what I mean. This code compiles using Visual C++ 2005 and outputs "B::fn()" and "C::fn()" correctly. With what compiler did you try it?
vividos
@vividos: Interesting. According to section 10.3.3 of the C++ standard, each virtual function in a class must have a unique final overrider to be "well-formed" -- meaning a compiler must emit an error message if that's not the case. But every compiler I tried (MSVC++9, MinGW, g++ 4.1.2 on Linux)...
j_random_hacker
... did allow your code to compile and run without problems. (I even tried Comeau C++, which is one of the stricter ones, and it worked too!) Compiler errors were produced only if an attempt was made to actually call the ambiguous function fn(). So technically speaking, all 4 compilers have bugs...
j_random_hacker
@Luc: Which compiler are you using that actually correctly diagnoses this as an error?
j_random_hacker
@vividos: Although I stand by my position of "the standard is always Right, compilers be damned!" I've decided to drop my -1, since if 3 separate implementations can get it wrong it's a bit unfair to expect us peasants to get it right... :)
j_random_hacker
The reason for this is maybe that the derived classes are template parameters. The code from litb gives a compiler error, too.
vividos
@vividos: Yes I thought that was it too -- but I tried it with a regular non-template class and everything still compiled and worked! :/
j_random_hacker
+1  A: 

You cannot enumerate the definitions of fn() in the ancestry. C++ lacks reflection. The only way I can imagine is a giant loop testing the typeid's of all possible ancestors. And it hurts to imagine that.

joelr
A: 

Vividos has already answered the main part of the post. Even if I would use the scope operator instead of the more cumbersome static_cast<> + dereference operator.

Depending on the task at hand, maybe you can change the inheritance relationship from D to B and C for a less coupling composition (plus possibly inheritance from A). This is assuming that you don't need D to be used polimorphically as either B or C, and that you don't really require B and C sharing the same base instance.

If you opt for composition, you can receive the B and C as arguments to your constructor as references/pointers of type A, making D completely unaware of the types B and C. At that point, you can use a container to hold as many A derived objects. Your own implementation of fn() (if you so decide) or any other method.

David Rodríguez - dribeas
+9  A: 

Unless you overwrite fn again in D, no it is not possible. Because there is no final overrider in a D object: Both C and B override A::fn. You have several options:

  • Drop either C::fn or B::fn. Then, the one that still overrides A::fn has the final overrider.
  • Place a final overrider in D. Then, that one overrides A::fn aswell as fn in C and B.

For example the following results in a compile time error:

#include <iostream>

class A {
public:
    virtual void fn() { }
};

class B : public virtual A {
public:
    virtual void fn() { }
};

class C : public virtual A {
public:
    virtual void fn() { }
};

// does not override fn!!
class D : public B, public C {
public:
    virtual void doit() {
        B::fn();
        C::fn();
    }
};

int main(int argc, char **argv) {
  D d;
  d.doit();
  return 0;
}

You can, however derive non-virtual from A in C and B, but then you have no diamond inheritance anymore. That is, each data-member in A appears twice in B and C because you have two A base-class sub-objects in an D object. I would recommend you to rethink that design. Try to eliminate double-objects like that that require virtual inheritance. It often cause such kind of conflicting situations.

A case very similar to this is when you want to override a specific function. Imagine you have a virtual function with the same name in B and C (now without a common base A). And in D you want to override each function but give different behavior to each. Depending whether you call the function with a B pointer or C pointer, you have the different behavior. Multiple Inheritance Part III by Herb Sutter describes a good way of doing that. It might help you decide on your design.

Johannes Schaub - litb
I think that's not what shoosh wants to do. He stated "What I'm trying to do is to have D somehow enumerate all of the instances of fn() it has in its ancestry.". He doesn't want to override fn() in class D.
vividos
that is the reason i stated it is not possible. if both C and B override A::fn, then he cannot define D without overriding fn in D
Johannes Schaub - litb
+1  A: 

You might want to look at Loki TypeLists if you really need to be able to track ancestry and enumerate through types. I'm not sure if what you are asking for is really possible without a bunch of work. Make sure that you aren't over-engineering here.

On a slightly different note, if you are going to use MI in this manner (i.e., the dreaded diamond), then you should be very explicit about which virtual member you want. I can't think of a good case where you want to choose the semantics of B::fn() over C::fn() without explicitly making a decision when writing D. You will probably pick one over the other (or even both) based on what the individual method does. Once you have made a decision, the requirement is that inherited changes do not change the expectations or semantic interface.

If you are really worried about swapping in a new class, say E in place of say B where E does not descend from B but offers the same interface, then you should really use the template approach though I'm not sure why there is a static_cast<> in there...

struct A {
    virtual ~A() {}
    virtual void f() = 0;
};
struct B: A {
    virtual void f() { std::cout << "B::f()" << std::endl; }
};
struct C: A {
    virtual void f() { std::cout << "C::f()" << std::endl; }
};

template <typename Base1, typename Base2>
struct D: Base1, Base2 {
    void g() { Base1::f(); Base2::f(); }
};

int main() {
    D<B,C> d1;
    D<C,B> d2;
    d1.g();
    d2.g();
    return 0;
}

// Outputs:
//   B::f()
//   C::f()
//   C::f()
//   B::f()

works fine and seems a little easier to look at.

D.Shawley