I would like to implement interactions between two objects whose types are derived from a common base class. There is a default interaction and specific things may happen once objects of the same type interact.
This is implemented using the following double dispatch scheme:
#include <iostream>
class A
{
public:
virtual void PostCompose(A* other)
{
other->PreCompose(this);
}
virtual void PreCompose(A* other)
{
std::cout << "Precomposing with an A object" << std::endl;
}
};
class B : public A
{
public:
virtual void PostCompose(A* other) // This one needs to be present to prevent a warning
{
other->PreCompose(this);
}
virtual void PreCompose(A* other) // This one needs to be present to prevent an error
{
std::cout << "Precomposing with an A object" << std::endl;
}
virtual void PostCompose(B* other)
{
other->PreCompose(this);
}
virtual void PreCompose(B* other)
{
std::cout << "Precomposing with a B object" << std::endl;
}
};
int main()
{
A a;
B b;
a.PostCompose(&a); // -> "Precomposing with an A object"
a.PostCompose(&b); // -> "Precomposing with an A object"
b.PostCompose(&a); // -> "Precomposing with an A object"
b.PostCompose(&b); // -> "Precomposing with a B object"
}
I have two, unfortunately quite different questions regarding this code:
Do you think this is a reasonable approach? Would you suggest something different?
If I omit the first two B methods, I get compiler warnings and errors that the last two B methods hide the A methods. Why is that? An A* pointer should not be cast to a B* pointer, or should it?
Update: I just found out that adding
using A::PreCompose;
using A::PostCompose;
makes the errors and warnings vanish, but why is this necessary?
Update 2: This is neatly explained here: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9, thank you. What about my first question? Any comments on this approach?
Double dispatch is usually implemented differently in C++, with the base class having all different versions (which makes it a maintenance nightmare, but that is how the language is). The problem with your attempt to double dispatch is that dynamic dispatch will find the most derived type B of the object on which you are calling the method, but then the argument has static type A*. Since A does not have an overload that takes B* as argument, then the call other->PreCompose(this) will implicitly upcast this to A* and you are left with single dispatch on the second argument.
As of the actual question: why is the compiler producing the warnings? why do I need to add the using A::Precompose directives?
The reason for that are the lookup rules in C++. Then the compiler encounters a call to obj.member(), it has to lookup the identifier member, and it will do so starting from the static type of obj, if it fails to locate member in that context it will move up in the hierarchy and lookup in the bases of the static type of obj.
Once the first identifier is found, lookup will stop and try to match the function call with the available overloads, and if the call cannot be matched it will trigger an error. The important bit here is that lookup will not look further up in the hierarchy if the function call cannot be matched. By adding the using base::member declaration, you are bringing the identifier member from the base class into the current scope.
Example:
struct base {
void foo( const char * ) {}
void foo( int ) {}
};
struct derived : base {
void foo( std::string const & ) {};
};
int main() {
derived d;
d.foo( "Hi" );
d.foo( 5 );
base &b = d;
b.foo( "you" );
b.foo( 5 );
d.base::foo( "there" );
}
When the compiler encounters the expression d.foo( "Hi" ); the static type of the object is derived, and lookup will check all member functions in derived, the identifier foo is located there, and lookup does not proceed upwards. The argument to the only available overload is std::string const&, and the compiler will add an implicit conversion, so even if there could be a best potential match (base::foo(const char*) is a better match than derived::foo(std::string const&) for that call) it will effectively call:
d.derived::foo( std::string("Hi") );
The next expression d.foo( 5 ); is processed similarly, lookup starts in derived and it finds that there is a member function there. But the argument 5 cannot be converted to std::string const & implicitly and the compiler will issue an error, even if there is a perfect match in base::foo(int). Note that this is an error in the call, not an error in the class definition.
When processing the third expression, b.foo( "you" ); the static type of the object is base (note that the actual object is derived, but the type of the reference is base&), so lookup will not search in derived but rather start in base. It finds two overloads, and one of them is a good match, so it will call base::foo( const char* ). The same goes for b.foo(5).
Finally, while adding the different overloads in the most derived class hide the overloads in the base, it does not remove them from the objects, so you can actually call the overload that you need by fully qualifying the call (which disables lookup and has the added side effect of skipping dynamic dispatch if the functions were virtual), so d.base::foo( "there" ) will not perform any lookup at all and just dispatch the call to base::foo( const char* ).
If you had added a using base::foo declaration to the derived class, you would add all the overloads of foo in base to the available overloads in derived, and the call d.foo( "Hi" ); would consider the overloads in base and find that the best overload is base::foo( const char* );, so it will actually be executed as d.base::foo( "Hi" );
In many cases, developers are not always thinking on how the lookup rules actually work, and it might be surprising that the call to d.foo( 5 ); fails without the using base::foo declaration, or worse, that the call to d.foo( "Hi" ); is dispatched to derived::foo( std::string const & ) when it is clearly a worse overload than base::foo( const char* ). That is one of the reasons why compilers warn when you hide member functions. The other good reason for that warning is that in many cases when you actually intended to override a virtual function you might end up mistakenly changing the signature:
struct base {
virtual std::string name() const {
return "base";
};
};
struct derived : base {
virtual std::string name() { // missing const!!!!
return "derived";
}
}
int main() {
derived d;
base & b = d;
std::cout << b.name() << std::endl; // "base" ????
}
A small mistake while trying to override the member function name (forgetting the const qualifier) means that you are actually creating a different function signature. derived::name is not an override to base::name and thus a call to name through a reference to base will not be dispatched to derived::name!!!
using A::PreCompose;
using A::PostCompose;
makes the errors and warnings vanish, but why is this necessary?
If you add new functions to your derived class with same name as your base class contains and if you don't override the virtual functions from base class, then new names hide the old names from the base class.
That is why you need to unhide them by explicity writing:
using A::PreCompose;
using A::PostCompose;
Other way to unhide them (in this particular case) is , override the virtual functions from base class which you've done in the code you've posted. I believe that code would compile just fine.
Classes are scopes and the look up in a base class is described as looking up in an enclosing scope.
When looking up overload of a function, looking up in enclosing scope is not done if a function was found in the nested one.
A consequence of the two rules is the behaviour you experimented. Adding the using clauses import the definition from the enclosing scope and is the normal solution.
Related
I'm experimenting with this code
class Base
{
public:
virtual void foo(int) {
// void foo(int) {
cout << "int" << endl;
}
};
class Derived : public Base
{
public:
void foo(double) {
cout << "double" << endl;
}
};
int main()
{
Base* p = new Derived;
p->foo(2.1);
Derived d;
d.foo(2); // why isn't this double?
return 0;
}
It's also available in online editor here https://onlinegdb.com/s8NwhfG_Yy
I'm getting
int
double
I don't understand why d.foo(2) calls the double version. Since Derived inherits the int method and has its own double method, wouldn't polymorphism dictate that 2 be matched to the parent? Thanks.
Polymorphism as in calling virtual methods is orthogonal to symbol and overload resolution. The former happens run-time, the rest at compile-time.
object->foo() always resolves the symbol at compile-time - member variable with overloaded operator() or a method. virtual only delays selecting "the body" of the method. The signature is always fixed, including the return value of course. Otherwise the type system would break. This is one of the reasons why there cannot be virtual function templates.
What you are actually experiencing is name hiding and overload resolution.
For Base* p; p->foo(2.1), the list of possible symbol candidates is only Base::foo(int). The compiler cannot know that p points to Derived (in general) because the choice must be done at compile time. Since int is implicitly convertible to double, foo(int) is chosen. Because the method is virtual, if Derived were to provide its own foo(int) override, it would have been called instead of Base::foo(int) at run-time.
For Derived*d; d->foo(2), the compiler first looks for symbol foo in Derived. Since there is foo(double) which is valid as foo(2), it is chosen. If there was no valid candidate, only then the compiler would look into base classes. If there was Derived::foo(int), possibly virtual, the compiler would choose this instead because it is a better match.
You can disable the name hiding by writing
class Derived: public Base{
using Base::foo;
};
It injects all Base::foo methods into Derived scope. After this, Base::foo(int) (now really Derived::foo(int)) is chosen as the better match.
Read Overload resolution. The answer is in Details:
If any candidate function is a member function (static or non-static), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.
The selecting between the methods void foo(int) and void foo(double) is happening like between the functions void foo(Base, int) and void foo(Derived, double). The second function while calling d.foo(2) is ranked higher.
If you add using Base::foo; in Derived, the resolution will be performed between three functions void foo(Base, int), void foo(Derived, int) and void foo(Derived, double), and int will be printed.
I've tried to map it out in my head, but honestly I have no idea what's really going on here.
What exactly is happening when I add and remove the virtual keyword from the below example?
#include <iostream>
#include <string>
class A {
public:
A() { me = "From A"; }
void caller() { func(); }
virtual void func() { std::cout << me << std::endl; } // THIS LINE!
private:
std::string me;
};
class B : public A {
public:
B() { me = "From B"; }
void func() { std::cout << me << std::endl; }
private:
std::string me;
};
int main() {
A a;
a.caller();
B b;
b.caller();
return 0;
}
With the virtual keyword, it prints "From A", then "From B".
Without the virtual keyword, it prints "From A", then "From A".
So far, this is the only time I've found a use for virtual functions without pointers being involved. I thought that if the virtual keyword was removed, the compiler would do the standard thing which is to overload the inherited function and end up printing "From A", and "From B" anyway.
I think this is deeper than just the VTable, and that it's more about the way it behaves in particular circumstances. Does B even have a VTable?
The call
func()
is equivalent to
this->func()
so there is a pointer involved.
Still, there's no need to involve pointers to understand the behavior.
Even a direct call of e.g. b.func() has to work as if it's a virtual call, when func is virtual in the statically known type. The compiler can optimize it based on knowing the most derived type of b. But that's a different kind of consideration (optimizations can do just about anything).
Apart from the issue of virtual dispatch, what may bring extra confusion, is that you have two mes, one declared in A and another declared in B. These are two distinct objects.
An object of type B has two data members of type std::string; one on its own, and one incorporated into the subobject of type A. The latter one, though, is not immediately available in the methods of type B because its name is eclipsed by the new me introduced in this class (though you may use a qualified name, A::me to refer to it).
Therefore, even though the bodies of A::func and B::func seem identical, the identifier me used in both of them refers to different members.
In your example, you won't see the difference:
With the virtual function, the compiler will generate a call via the VTable and at runtime, each objects will call the right function for their real class.
With the non virtual function, the compiler determines at compile time the right function to call, based on the objects defined class.
Now try the following, to see the virtual function in action:
A *pa = &b; // pointer to an A: valid as b is a B wich is also an A.
pa -> caller(); // guess what will be called if virtual or not.
No need for pointer to experimenting with virtual functions. You can observe the same effect with references as well:
A& ra = b; // create a reference to an A, but could as well be a parameter passed by reference.
ra.caller();
Virtual functions are useful for polymorphism. The idea is that you work with a general object of a class, but you don't know at compile time, if at runtime the object will really be of this class, or if it will not be a more specialiszed object (inheriting from the class).
This is basically a copy from the example given in Item 21. Overriding Virtual Functions in Herb Sutter's book Exceptional C++.
#include <iostream>
#include <complex>
using namespace std;
class Base
{
public:
virtual void f(int);
virtual void f(double);
virtual ~Base() {};
};
void Base::f(int) { cout << "Base::f(int)" << endl; }
void Base::f( double ) { cout << "Base::f(double)" << endl; }
class Derived: public Base {
public:
void f(complex<double>);
};
void Derived::f(complex<double>) { cout << "Derived::f(complex)" << endl; }
int main()
{
Base* pb = new Derived;
pb->f(1.0);
delete pb;
}
The code prints Base::f(double) and I have no problems with that. But I couldn't understand the explanation given by the author on the top of page 122 (emphasis is mine):
Interestingly, even though the Base* pb is pointing to a Derived
object, this calls Base::f( double ), because overload resolution is
done on the static type (here Base), not the dynamic type (here
Derived).
My understanding is that the call pb->f(1.0) is a virtual call and Base::f(double) is the final overrider for f(double) in Derived. What does that have to do with function overloading?
The delicate part here is that virtual methods are a mechanism to dispatch the function call, while overloading is a feature that affects the resolution of the call.
That is, for any call the compiler needs to figure out which method should be called (resolve it); afterwards, and in a logically distinct operation, it needs to generate code that calls the correct implementation of that method (dispatch it).
From the definitions of Base and Derived given above we can easily reason that if f(double) is called on a Base* then the call should be dispatched to any derived override (if applicable) in preference to the base implementation. But answering that is answering a question totally different than
When the source says pb->f(1.0), which of the methods named f
should be used to resolve the method call?
As Sutter explains, the spec says that when resolving the call the compiler will look at the methods named f declared on the static type pointed to by pb; in this case, the static type is Base* so overloads (not overrides!) declared on Derived will not be considered at all. However, if the method that the call resolves to is virtual then the possible implementation provided on Derived will be used as expected.
The reason this example is interesting is because, if pb were a Derived* instead of a Base*, or if the compiler could somehow use the dynamic type rather than the static type for performing overload resolution, it would match the call to pb->f(1.0) to void Derived::f(complex<double>) (complex<double> can be implicitly constructed from double). This is because the presence of a function named f in a derived class effectively hides any base class overloads with the same name, even if their argument lists are different. But since the static type of pb is actually Base*, this doesn't happen.
In this example, despite the repeated occurrence of virtual, there is no method overriding at all; the method f in the derived class overrides neither of the ones in the base class, because the argument types do not match. Given this circumstance, there is no way the call of pb->f could ever invoke the (unique) method Derived::f. Overload resolution/name lookup (which only considers methods of the static type of pb->f) must choose between the two methods declared as Base::f, and in the example it will choose the one with argument type double. (At runtime this might end up calling an override, if one is defined in a different derived class than Derived, and if the example is modified so that pb could possibly point to an object of such another derived class.)
A separate issue is that the methods named f in Base and Derived would not be simultaneously considered for overload resolution if f is called from an expression of (static) type Derived either, this time because the methods in the base class are hidden by the declaration of f in Derived, so they are not available for such calls. I think this hiding can be avoided by declaring using Base::f;,which "lifts" the methods Base::f into Derived as if they were also declared there (but I admit not knowing the details of this mechanism; I suppose the lifts would then be overrides of the virtual base method with the same argument type, but it makes little difference because the lifts refer to the implementation in the base class anyway.)
Please definitively explain what the method resolution rules are in the following case. I have an idea, based on the behavior of the code, but would like clarification.
Based on "The const-ness of the calling object determines which version of MyArray::Get() will be invoked and thus whether or not the caller is given a reference with which he can manipulate or only observe the private data in the object. The two methods technically have different signatures because their "this" pointers have different types, allowing the compiler to choose the right one" from wikipedia const correctness, I would conclude that my example should be a case of method overloading, not method overriding (since a const method and a non-const method have two different signatures).
class Base
{
public:
void test()
{ std::cout << "nonconst call" << std::endl; }
};
class Child : public Base
{
public:
void test() const
{
std::cout << "const call" << std::endl;
Child * nonConstThis = const_cast<Child * >(this);
Base * nonConstBase = dynamic_cast<Base * >(nonConstThis);
// This call leads to infinite recursion by calling
// "void Child::test() const", which implies that
// a "Child *" will resolve to calling a const Child function
// before calling a non-const Base function.
//nonConstThis->test();
// This will call "void Base::test()"
nonConstBase->test();
}
};
void main()
{
Child * child = new Child;
child->test();
}
It's actually method hiding, not overloading.
When you create a method with the same name in a derived class, the base class version is no longer visible.
struct A
{
void foo() {}
};
struct B : public A
{
void foo(int x) {}
};
B b;
b.foo(); //error
I'm assuming you expect B::foo() to exist, but, as you can see, it does not. So nothing, not cv-qualifiers (const) or parameters affect this.
In your case the name isn't resolved because it has something to do with the const, but because you're calling test on an object of type Child. You then call Base::test() on an object of type Base, just like the following would work form my example:
((A)b).foo();
You are stumbling on how overload name resolution works in C++. The function "test" in the base class becomes "hidden" (it's called name hiding). When looking for a suitable function to call, the compiler looks in the derived class first, sees a match, then stops looking. This answer has a good description as to why.
You can use a using declaration to look in the base class as well like this:
class Child : public Base
{
public:
using Base::test;
this will tell the compiler to also look in Base for test.
I've got a situation where it seems like the compiler isn't finding the base class definition/implementation of a virtual function with the same name as another member function.
struct One {};
struct Two {};
struct Base
{
virtual void func( One & );
virtual void func( Two & ) = 0;
};
struct Derived : public Base
{
virtual void func( Two & );
};
void Base::func( One & )
{}
void Derived::func( Two & )
{}
// elsewhere
void this_fails_to_compile()
{
One one;
Derived d;
d.func( one );
}
I'm using Visual C++ 2008. The error message is:
error C2664: 'Derived::func' : cannot convert parameter 1 from 'One' to 'Two &'
I would have thought that the type based dispatch would work and call the defined base class function. If I add a Derived::func( One & ) it does compile and get called correctly, but in my situation, that version of the function can be done in the base class and usually derived classes don't need to implement it themselves. I'm currently working around it by putting a differently named, non-virtual function in the base class that forwards the call to function causing the problem:
// not virtual, although I don't think that matters
void Base::work_around( One & one )
{
func( one );
}
That works but is obviously less than ideal.
What inheritance and/or name-hiding rule am I missing here?
You are hiding the method in the derived class. The simplest solution is to add a using declaration to the derived class.
struct Derived : public Base
{
using Base::func;
virtual void func( Two & );
};
The issue is that when the compiler tries to lookup the func identifier in the call d.func(one) it has to do that from Derived upwards, but it will stop in the first context where it finds the func identifier, which in this case it is Derived::func. No further lookup is performed and the compiler was seeing only the Derived::func( Two& ).
By adding the using Base::func; directive, when the compiler sees the Derived definition it brings all of Base::func declarations into scope, and it will find that there is a Base::func( One & ) that was not overridden in Derived.
Note also that if you were calling through a reference to Base, then the compiler would find both overloads of func and would dispatch appropriately each one to the final overrider.
Derived d;
Base & b = d;
b.func( one ); // ok even without the 'using Base::func;' directive
You hide func(One&) function in Derived. You could use fully qualified name:
d.Base::func( one );