I'm learning about multiple inheritance and the diamond problem, and when I make a function call from the most derived class Visual Studio tells me that the call is ambiguous:
struct A
{
virtual void aFunction() { cout << "I am A\n"; }
};
struct B : A {};
struct C : A {};
struct D : B, C {};
int main()
{
D DObj;
DObj.aFunction(); // This is an ambiguous call
}
I understand if I had overridden the base class function in the B and C classes then the call would be ambiguous, but isn't "aFunction()" the same in B and C?
Also, making B and C inherit from A virtually makes the error go away. But my understanding of the keyword "virtual" when inheriting, ie., (Derived : virtual Base), is that it prevents a "more derived class" further down the chain from inheriting multiple copies of a Base up the chain. In inheritance multiple copies of member variables can be inherited, but only one copy of a function with the same name. So for example I could have 5 Derived classes each deriving from Base, and then a MostDerivedClass inheriting from all the 5 Derived classes, in the MostDerivedClass I would have 5 copies of the Base class "member variables", but only one copy of a function with the same name.
So in other words the "virtual" keyword for inheritance should prevent multiple Base "member variable" copies. I don't understand why it would clear up an ambiguous function call in this case.
EDIT: Thank you, it's slowly sinking in. It was impossible for me to imagine "two copies" of A in D, because A is empty (no size). But then I remembered that C++ never creates empty classes, on my setup for example an empty class has size 1. Then I was able to imagine "two copies" of A in D, and it's starting to make sense now.
The call is ambiguous because there are two possible A base objects that could be passed as the this argument of the call. Even though it is the same physical function that ulitimately get called, and that function completely ignores its this argument, the fact that there are two of them makes it ambiguous.
Using virtual inheritance means that there would be only one A base object, so then the call would not be ambiguous.
Because multiple copies of member variables are inherited, you could have two separate copies of a function with different behavior.
struct A
{
int x;
virtual void aFunction() { cout << "I am A with value " << x ; }
};
struct B : A {
};
struct C : A {
};
struct D : B, C {};
int main()
{
D DObj;
((B*)(&DObj))->x = 0; // set the x in B to 0
((C*)(&DObj))->x = 1; // set the x in C to 1
DObj.aFunction(); // This is an ambiguous call
}
Should this output 0 or 1?
The compiler could detect the specific case of an inline function that does not reference this, but you can easily work around the issue so its not worth the complexity for a relatively rare case.
use the virtual inheritance to solve the diamond prolem:
struct A
{
int x;
virtual void aFunction() { cout << "I am A with value " << x ; }
};
struct B : virtual A { // add virtual
};
struct C : virtual A { // virtual
};
struct D : B, C {};
Related
I'm learning C++ and after having read and tested a lot about multiple inheritance, virtual inheritance/methods and diamond design I still have some problems to understand it till the end.
I'm in the diamond design pattern, where class B e C inherit virtual public A and D inherits public B, public C:
A
/ \
B C
\ /
D
All classes implement a private variable std::string _message that I initialize with the following string.
"class-name instance"
Only Class A implements a virtual public display(void) const method too.
void display(void) const{
std::cout << this->_message << std::endl;
}
The main is:
D foo;
foo.display();
The output is:
A instance
However when I "inspect" the element debugging step by step with Xcode I see that inside the D instance I can correctly find a B, a C and A object (just one shared by B and C) with all the different _message correctly assigned.
What I'm doing wrong? Need I override the display() method in D class? But if so, which is the real point of multiple inheritance if I have to re-implements the same method in a derived class?
Your question seems to not be related to the diamond pattern, but is generally about the inheritance model in C++. Most things in C++ are statically bound, so at compile time the compiler fixes what method or member of a certain name is used:
If you access a member of the implicit this object or using a pointer or reference to an object, you will end up accessing a member of the class the pointer or reference has at compile time, no matter whether at runtime there is a derived-class object that has members of the same name. That's why they say you can not override but just shadow members in base classes when you define an equal named member in the derived class.
The same thing is true for non-virtual functions.
The only thing that behaves different is virtual functions. These functions can be overwritten in derived classes, and code that gets handed a pointer-to-base-class can virtual functions of the base class and end up executing implementations given in the derived class.
So the point of virtual functions is not to have the compiler reinterpret a certain function in the context of derived classes (as you seem to understand it), but to make it possible to replace a function in the base class by a different function in the derived class.
To go back to your motivation: If you want a display function that prints a message that depends on the actual object type, the thing that is fixed is the printig, which doesn't need to be virtual. But you need a virtual function to obtain the object type. Like this:
#include <iostream>
#include <ostream>
class A {
public:
void display() { std::cout << get_message() << '\n'; }
virtual const char * get_message() { return "A instance"; }
};
class B : virtual public A {
public:
virtual const char * get_message() { return "B instance"; }
};
class C : virtual public A {
public:
virtual const char * get_message() { return "C instance"; }
};
class D : public B, public C {
public:
// virtual const char * get_message() { return B::get_message(); }
// virtual const char * get_message() { return C::get_message(); }
// virtual const char * get_message() { return "D instance"; }
};
int main(void)
{
D foo;
foo.display();
A* a_ptr = &foo;
a_ptr->display();
return 0;
}
The example as given will not compile (now this is due to the diamond pattern), because the compiler can not decide, what overrider of A::get_message(), either B::get_message() or C::get_message() should be picked in D objects, you need to make one of the comments in D real code to declare the one-and-only get_message for D, in which can re-use the existing functions.
Yesterday, I wrote some code and i would really appreciate a judgement if this is good or bad practice. And if its bad, what could go wrong.
The construct is as followed:
The base class A comes from an API sadly as a template. The goal was to be able to put derived classes from A in a std::vector. I achieved this with the base class B from which all derived classes that inherit from A will inherit. Without the pure-virtual function in B the foo() function from A was called.
With the pure-virtual function in B the foo() version from C get called. This is exactly what I want.
So is this bad practice?
EDIT:
To clear some misunderstandings in my example code out:
template <class T>
class A
{
public:
/* ctor & dtor & ... */
T& getDerived() { return *static_cast<T*>(this); }
void bar()
{
getDerived().foo(); // say whatever derived class T will say
foo(); // say chicken
}
void foo() { std::cout << "and chicken" << std::endl; }
};
class B : public A<B>
{
public:
/* ctor & dtor */
virtual void foo() = 0; // pure-virtual
};
class C : public B
{
public:
/* ctor & dtor */
virtual void foo() { std::cout << "cow"; }
};
class D : public B
{
public:
/* ctor & dtor */
virtual void foo() { std::cout << "bull"; }
};
The std::vector shall contain both, C as well as D and
void main()
{
std::vector<B*> _cows;
_cows.push_back((B*)new C()));
_cows.push_back((B*)new D()));
for(std::vector<B*>::size_type i = 0; i != _cows.size(); ++i)
_cows[i].bar();
}
with output
cow and chicken
bull and chicken
is desired.
As far as I know is there no other way to store classes derived from a template class in a container than the use of a base class. If I use a base class for my derived classes, e.g., B i must cast every instance within the vector back to its proper class. But at the time bar() is called, I don't know the exact type.
I call it bad practice for doing possibly confusing and obfuscating things.
1) A function name should in all base and sub-classes either be virtual or non-virtual.
mixing overriding and overloading within the inheritance hierachry is a very bad idea.
No one really expects something like that.
Thus if A::foo should be virtual as well, or B and C foo should not.
2) Preferably base classes should be interfaces.
As such A:foo should be pure virtual and not B::foo.
2b) If a base class has already provided a default implementation don't declare the function pure virtual in a sub-class.
I made a class with virtual function f() then in the derived class I rewrote it like the following f(int) why can't I access the base class function throw the child instance ?
class B{
public:
B(){cout<<"B const, ";}
virtual void vf2(){cout<<"b.Vf2, ";}
};
class C:public B{
public:
C(){cout<<"C const, ";}
void vf2(int){cout<<"c.Vf2, ";}
};
int main()
{
C c;
c.vf2();//error should be vf2(2)
}
You have to do using B::vf2 so that the function is considered during name lookup. Otherwise as soon as the compiler finds a function name that matches while traversing the inheritance tree from child -> parent -> grand parent etc etc., the traversal stops.
class C:public B{
public:
using B::vf2;
C(){cout<<"C const, ";}
void vf2(int){cout<<"c.Vf2, ";}
};
You are encountering name hiding. Here is an explanation of why it happens ?
In C++, a derived class hides any base class member of the same name. You can still access the base class member by explicitly qualifying it though:
int main()
{
C c;
c.B::vf2();
}
You were caught by name hiding.
Name hiding creeps up everywhere in C++:
int a = 0
int main(int argc, char* argv[]) {
std::string a;
for (int i = 0; i != argc; ++i) {
a += argc[i]; // okay, refers to std::string a; not int a;
a += " ";
}
}
And it also appears with Base and Derived classes.
The idea behind name hiding is robustness in the face of changes. If this didn't exist, in this particular case, then consider what would happen to:
class Base {
};
class Derived: public Base {
public:
void foo(int i) {
std::cout << i << "\n";
}
};
int main() {
Derived d;
d.foo(1.0);
}
If I were to add a foo overload to Base that were a better match (ie, taking a double directly):
void Base::foo(double i) {
sleep(i);
}
Now, instead of printing 1, this program would sleep for 1 second!
This would be crazy right ? It would mean that anytime you wish to extend a base class, you need to look at all the derived classes and make sure you don't accidentally steal some method calls from them!!
To be able to extend a base class without ruining the derived classes, name hiding comes into play.
The using directive allows you to import the methods you truly need in your derived class and the rest are safely ignored. This is a white-listing approach.
When you overload a member function in a base class with a version in the derived class the base class function is hidden. That is, you need to either explicitly qualify calls to the base class function or you need a using declaration to make the base class function visible via objects of the derived class:
struct base {
void foo();
void bar();
};
struct derived: base {
void foo(int);
using base::foo;
void bar(int);
};
int main() {
derived().foo(); // OK: using declaration was used
derived().bar(); // ERROR: the base class version is hidden
derived().base::bar(); // OK: ... but can be accessed if explicitly requested
}
The reason this is done is that it was considered confusing and/or dangerous when a member function is declared by a derived function but a potenially better match is selected from a base class (obviously, this only really applies to member functions with the same number of arguments). There is also a pitfall when the base class used to not have a certain member function: you don't want you program to suddenly call a different member function just because a member function is being added to the base class.
The main annoyance with hiding member functions from bases is when there is a set of public virtual functions and you only want to override one of them in a derived class. Although just adding the override doesn't change the interface using a pointer or a reference to the base class, the derived class can possibly not used in a natural way. The conventional work-around for this to have public, non-virtual overload which dispatch to protected virtual functions. The virtual member function in the various facets in the C++ standard library are an example of this technique.
I wanted to have confirmation about the following things:
Virtual Mechanism:
I f I have a base class A and it has a Virtual method, then in the derived class generally, we do not include the virtual statement in the function declaration. But what does a virtual mean when included at the dervied class definition.
class A
{
public:
virtual void something();
}
class B:public A
{
public:
virtual void something();
}
Does, that mean that we want to override the method somethign in the classes that derive from the class B?
Also, another question is,
I have a class A, which is derived by three different classes.Now, there is a virtual method anything(), in the base class A.
Now, if I were to add a new default argument to that method in the base class, A::anything(), I need to add it in all the 3 classes too right.
My pick for the answers:
If a method which is virtual in the base class is redefined in the derived class as virtual then we might mean that it shall be overridden in the corresponding derived classes which uses this class as base class.
Yes.If not overriding does not have any meaning.
Pls let me know if what I feel(above 2) are correct.
Thanks,
Pavan Moanr.
The virtual keyword can be omitted on the override in the derived classes. If the overridden function in the base class is virtual, the override is assumed to be virtual as well.
This is well covered in this question: In C++, is a function automatically virtual if it overrides a virtual function?
Your second question is about default values and virtual functions. Basically, each override can have a different default value. However, usually this will not do what you expect it to do, so my advice is: do not mix default values and virtual functions.
Whether the base class function is defaulted or not, is totally independent from whether the derived class function is defaulted.
The basic idea is that the static type will be used to find the default value, if any is defined. For virtual functions, the dynamic type will be used to find the called function.
So when dynamic and static type don't match, unexpected results will follow.
e.g.
#include <iostream>
class A
{
public:
virtual void foo(int n = 1) { std::cout << "A::foo(" << n << ")" << std::endl; }
};
class B : public A
{
public:
virtual void foo(int n = 2) { std::cout << "B::foo(" << n << ")" << std::endl; }
};
int main()
{
A a;
B b;
a.foo(); // prints "A::foo(1)";
b.foo(); // prints "B::foo(2)";
A& ref = b;
ref.foo(); // prints "B::foo(1)";
}
If all your overrides share the same default, another solution is to define an additional function in the base class that does nothing but call the virtual function with the default argument. That is:
class A
{
public:
void defaultFoo() { foo(1); }
virtual void foo(int n) { .... }
};
If your overrides have different defaults, you have two options:
make the defaultFoo() virtual as well, which might result in unexpected results if a derived class overload one but not the other.
do not use defaults, but explicitly state the used value in each call.
I prefer the latter.
It doesn't matter whether you write virtual in derived class or not, it will always be virtual because of the base class, however it is still better to include virtual to explicitly state that it is virtual and then if you accidentally remove that keyword from base class it will give you compiler error (you cannot redefine non-virtual function with a virtual one). EDIT >> sorry, I was wrong. You can redefine non-virtual function with a virtual one however once it's virtual all derived classes' functions with same signature will be virtual too even if you don't write virtual keyword. <<
If you don't redefine virtual function then the definition from base class will be used (as if it were copied verbatim).
If you wish to specify that a virtual function should be redefined in dervied class you should not provide any implementation i.e. virtual void something() = 0;
In this case your class will be an abstract base class (ABC) and no objects can be instantiated from it. If derived class doesn't provide it's own implementetian it will also be an ABC.
I'm not sure what do you mean about default arguments but function signatures should match so all parameters and return values should be the same (it's best to not mix overloading/default arguments with inheritance because you can get very surprising results for example:
class A
{
public:
void f(int x);
};
class B:public A
{
public:
void f(float x);
};
int main() {
B b;
b.f(42); //this will call B::f(float) even though 42 is int
}
Here is a little experiment to test out what you want to know:
class A {
public:
virtual void func( const char* arg = "A's default arg" ) {
cout << "A::func( " << arg << " )" << endl;
}
};
class B : public A {
public:
void func( const char* arg = "B's default arg" ) {
cout << "B::func( " << arg << " )" << endl;
}
};
class C : public B {
public:
void func( const char* arg ) {
cout << "C::func( " << arg << " )" << endl;
}
};
int main(int argc, char* argv[])
{
B* b = new B();
A* b2 = b;
A* c = new C();
b->func();
b2->func();
c->func();
return 0;
}
result:
B::func( B's default arg )
B::func( A's default arg )
C::func( A's default arg )
conclusion:
1- virtual keyword before A's func declaration makes that function virtual in B and C too.
2- The default argument used is the one declared in the class of pointer/reference you are using to access the object.
As someone pointed out, a function in a derived class with the same name and type signature as a virtual function in the base class is automatically always a virtual function.
But your second question about default arguments is interesting. Here is a tool for thinking through the problem...
class A {
public:
virtual void do_stuff_with_defaults(int a = 5, char foo = 'c');
};
is nearly equivalent to this:
class A {
public:
virtual void do_stuff_with_defaults(int a, char foo);
void do_stuff_with_defaults() { // Note lack of virtual keyword
do_stuff_with_defaults(5, 'c'); // Calls virtual function
}
void do_stuff_with_defaults(int a) { // Note lack of virtual keyword
do_stuff_with_defaults(a, 'c'); // Calls virtual functions
}
};
Therefore you are basically having virtual and non-virtual functions with the same name but different type signatures declared in the class if you give your virtual function default arguments.
On way it isn't equivalent has to do with being able to import names from the base class with the using directive. If you declare the default arguments as separate functions, it's possible to import those functions using the using directive. If you simply declare default arguments, it isn't.
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?
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.
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.
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.
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.
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.
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
How can I avoid the Diamond of Death when using multiple inheritance?
What is the exact problem with multiple inheritance?
Is Multiple Inheritance Evil?