mkln
mkln

Reputation: 14953

Calling overloaded function from within another function with parent class argument

I am trying to find a way to make something similar to this work:

class A {
public:
  int x;
};

class A1 : public A {
public: 
  int y;
};

class A2 : public A {
public:
  std::string s;
};

void printer(A1 A1obj){
  std::cout << (A1obj.y+1) << std::endl;
}

void printer(A2 A2obj){
  std::cout << A2obj.s << std::endl;
}

void printall(A Aobj){
  printer(Aobj);
}

In words: I have some code that works with A objects. many functions in my code take A objects as arguments, making no use of the y or s components. however eventually they will call a couple of functions that will behave differently according to whether the input A was really A1 or A2. I was thinking that I could "overload" these, considering both A1 and A2 are children of A. But the above code says no matching function for call to printer(A&)

One solution is to duplicate printall into printall(A1 A1obj) and printall(A2 A2obj) and use function overloading, but this would mean duplication of a many lines of code in my case, so I'd like to avoid it. Are there alternatives other than just merging A1 and A2 into A and, say, create a std::string label inside it to be used with if-else statements?

Upvotes: 0

Views: 1265

Answers (2)

aschepler
aschepler

Reputation: 72311

Option 1: Virtual functions ("run-time polymorphism")

Whatever makes sense as a conceptual thing to do with/to an A, but the details of what that means will vary by specific class, should be a virtual function of A.

class A {
public:
  virtual ~A() = default;
  // Disable copying to avoid accidental slicing:
  A(const A&) = delete;
  A& operator=(const A&) = delete;

  virtual void print() const = 0;

  int x;
};

class A1 : public A {
public: 
  void print() const override;
  int y;
};
class A2 : public A {
public:
  void print() const override;
  std::string s;
};

void A1::print() const {
  std::cout << y+1 << std::endl;
}
void A2::print() const {
  std::cout << s << std::endl;
}

void printall(const A& Aobj) {
  Aobj.print();
}

Each derived class A1 and A2 overrides the virtual function declared in A, so calling the function via an A reference or pointer will actually call the definition from the derived class.

Option 2: A template function ("compile-time polymorphism")

// Classes and printer overloads as in question

template <class T>
void printall(const T& Aobj) {
  printer(Aobj);
}

Define a template function which can take an object of any type, and generates a function for that type as needed. Here T could really be anything at all, not necessarily a class which inherits A, as long as it can be passed to some printer function.

But if there are other printall functions and that declaration makes it too greedy in overload resolution, you can restrict it to only take types that inherit A.

If you can use C++20 "constraints and concepts":

#include <concepts>

template <class T> requires std::derived_from<T, A>
void printall(const T& Aobj) {
  printer(Aobj);
}

Otherwise, you'd need something a little trickier:

// C++11 or later:
#include <type_traits>

template <class T>
typename std::enable_if<std::is_base_of<A, T>::value>::type
printall(const T& Aobj) {
  printer(Aobj);
}

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118330

This has nothing to do with ambiguous overloading. Even if there was only one printer() function, and one child class, this will never work. If you have a parent class, A, it cannot be implicitly converted to any child class, like A1. You can implicitly convert a child class to a parent class, but not the other way around. This is the only way C++ works.

The correct way is to define an abstract print() method in the A superclass, and then implement it in both A1 and A2 subclasses, to invoke the appropriate printer() function. This is precisely why virtual methods exist. Or, just implement each printer() in the subclass directly, as a print() method.

Upvotes: 1

Related Questions