I asked a question earlier but it turns out my problem was not properly modeled by my example. So here is my actual problem:
A
, and class B
inheriting from A
,foo(A&)
and foo(B&)
,A*
pointers, containing instances of A
and B
.foo(A&)
for instances of A
and foo(B&)
for instances of B
? Constraints: I can modify A
and B
implementation, but not foo
's implementation.See below an example:
#include <iostream>
#include <list>
class A {
public:
};
class B : public A {
public:
};
void bar(A &a) { std::cout << "This is an A" << std::endl; }
void bar(B &b) { std::cout << "This is a B" << std::endl; }
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
bar(**it);
}
Although I am using a container with pointers, bar
is called with object from the parent class, not the child class:
# ./a.out
This is an A
This is an A
#
I was expecting
This is a B
Passing pointers to bar
(by rewriting its signature) does not help.
Thx to Antonio for helping clarifying the question.
The function call operator, when overloaded, does not modify how functions are called. Rather, it modifies how the operator is to be interpreted when applied to objects of a given type.
Restrictions on overloadingAny two functions in a set of overloaded functions must have different argument lists. Overloading functions that have argument lists of the same types, based on return type alone, is an error.
The process of matching function calls to a specific overloaded function is called overload resolution. Just because there is no exact match here doesn't mean a match can't be found -- after all, a char or long can be implicitly type converted to an int or a double .
Using different parameters for the functions of same name to perform different types of tasks is known as function overloading.
Since overloading is resolved at compile time, you need to supply the compiler with enough information to decide on the proper overload of bar
to call. Since you wish to make that decision dynamically based on the run-time type of the object, virtual functions would be of great help:
struct A {
virtual void bar() { bar(*this); }
};
struct B : public A {
virtual void bar() { bar(*this); }
};
It may seem like the bodies are identical, so B::bar
could be eliminated, but this is not true: although the bodies look exactly the same, they call different bar
s due to the static resolution of overloads in C++:
A::bar
the type of *this
is A&
, so the first overload is called.B::bar
the type of *this
is B&
, so the second overload is called.Modify the calling code to call the member bar
will complete the change:
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
Edit: This answer the first version of the question, now see instead dasblinkenlight's solution.
If you do:
A* b = B();
Then *b
will be of type A. That's what you are doing in your for cycle. There's no "virtuality" or polimorfism involved in this.
The following code gives the behaviour you are looking for:
class A {
public:
virtual void bar() { std::cout << "This is an A" << std::endl; }
};
class B : public A {
public:
virtual void bar() { std::cout << "This is a B" << std::endl; }
};
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
l.push_back(new A());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
}
Taking my example above, in that case:
b->bar();
will print This is a b
.
You are looking for run-time polymorphism. This is supported "naturally" for virtual member methods.
An alternative would be to use RTTI and dynamically cast A*
to B*
and call bar
upon success... or static_cast
if you are really sure there are B*
objects. Generally need to down-cast indicates problematic design.
Important note: Run-time check in dynamic_cast requires the type to be polymorphic anyways. Maybe your particular A
fulfills this but you just can't change the class. If not, static_cast
is the only option available.
If you have control over class you, can use standard polymorphism and overload mechanisms using virtual methods on this
as a facade for the "external" call:
#include <iostream>
#include <list>
class A;
void external_bar(A&);
class A {
public:
virtual void bar() { external_bar(*this); };
};
class B;
void external_bar(B&); //IMPORTANT
class B : public A {
public:
virtual void bar() { external_bar(*this); };
};
void external_bar(A &a) { std::cout << "This is an A" << std::endl; }
void external_bar(B &b) { std::cout << "This is a B" << std::endl; }
int main(int argc, char **argv) {
std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
(*it)->bar();
}
This also has drawbacks. Forward declarations are needed. And you need to take care everything is defined properly, because if you forget line // IMPORTANT
the compiler will pick up the definition of external_bar
for A&
as it is implicitly convertible, and you might get quite a headache spotting the error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With