For an overloaded function, calling the specialized version for parent and child instances

I asked a question before, but it seems my problem was not modeled correctly in my example. So here is my actual problem:

  • I have class A and class B inheriting from A ,
  • I have two functions foo(A&) and foo(B&) ,
  • I have a list of pointers A* containing instances of A and B
  • How do I get a call to foo(A&) for instances of A and foo(B&) for instances of B ? Limitations: I can change the implementation of A and B , but not foo .

The following is 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 use a container with pointers, bar is called with an object from the parent class, and not with a child class:

 # ./a.out This is an A This is an A # 

I expected

 This is a B 

Passing pointers to bar (by overwriting its signature) does not help.

thanks to Antonio for helping clarify the issue.

+6
source share
5 answers

Since overloading is allowed at compile time, you need to provide the compiler with enough information to decide on the correct bar overload for the call. Since you want to make this decision dynamically based on the runtime type of an object, virtual functions will be very useful:

 struct A { virtual void bar() { bar(*this); } }; struct B : public A { virtual void bar() { bar(*this); } }; 

It might seem that the bodies are identical, so B::bar can be eliminated, but this is not true: although the bodies look exactly the same, they call different bar due to the static resolution of overloads in C ++:

  • Inside A::bar the *this type is A& , so the first overload is called.
  • Inside B::bar the *this type is B& , so a second overload is called.

Change the calling code to call the member bar will perform 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(); 
+5
source

Edit: This answer is to the first version of the question, now see instead of the dasblinkenlight solution .


If you do:

 A* b = B(); 

Then *b will be of type A. This is what you do in your loop. There is no "virtuality" or polymorphism in this.

The following code gives the behavior 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 this case:

 b->bar(); 

prints This is ab .

+4
source

You are looking for runtime polymorphism. This is supported "naturally" for virtual member methods.

An alternative would be to use RTTI and dynamically drop A* to B* and call bar after success ... or static_cast if you are really sure that there are B* objects. Typically, for a down image, a problematic design is displayed.

Important Note : checking dynamic_cast runtime requires the type to be polymorphic. Your particular A do this, but you just can't change the class. If not, static_cast is the only option available.

If you have control over the class, you can use the standard mechanisms of polymorphism and overloading, using virtual methods on this as a facade for an "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(); } 

It also has disadvantages. Advanced declarations required. And you need to take care that everything is correctly defined, because if you forget the // IMPORTANT line, the compiler will pick up the definition of external_bar for A& , since it is implicitly converted, and you can get quite a headache when it finds an error.

+2
source

Others have already explained how this can be achieved.

I will simply limit myself to why this is so.

B is implicitly given in here. Thus, currently it has only properties A.

Upstream casting is implied in C ++.

Downcasting in C ++ is only possible if your base class is polymorphic.

In short, a polymorphic requirement is nothing more than something in your base class that can be overridden by your derivative !! Virtual methods

then you can use RTTI and dynamic_cast, as prescribed by others, to do this.

Example:

 #include <iostream> #include <list> class A { public: virtual void dummy() = 0; }; class B : public A { public: void dummy() { } }; 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()); //Prints A for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it) bar(**it); //Prints B for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it) bar(dynamic_cast<B&>(**it)); } Answer: This is an A This is an A This is a B This is a B 

Note. This is only if there are objects of type B in your list. Otherwise, it will fail. This only explains that upcast vs downcast

+1
source

Antonio wrote a good solution involving virtual functions. If for some reason you really don't want to use a virtual function, you can use dynamic_cast in a free function:

 #include <iostream> #include <list> struct A { virtual ~A() {} // important }; struct B : A {}; void bar(A &a) { std::cout << "This is an A" << std::endl; } void bar(B &b) { std::cout << "This is a B" << std::endl; } void bar_helper(A *ptr) { if ( auto b = dynamic_cast<B *>(ptr) ) bar(*b); else bar(*ptr); } int main() { std::list<A *> ls; ls.push_back(new B); ls.push_back(new B); ls.push_back(new A); for (auto ptr : ls) { bar_helper(ptr); delete ptr; } ls.clear(); } 
0
source

Source: https://habr.com/ru/post/984781/


All Articles