4

I have the following code:

#include <iostream>
#include "boost/shared_ptr.hpp"

using boost::shared_ptr;

class Base {
 public:
  virtual ~Base() {}
  virtual void print() = 0;
};

template <typename T>
class Child : public Base {
 public:
  virtual void print() {
    std::cout << "in Child" << std::endl;
  }
};

class GrandChild : public Child<int> {
 public:
  virtual void print() {
    std::cout << "in GrandChild" << std::endl;
  }
};

template <typename T>
void call_print(shared_ptr<Child<T> > a) {
  a->print();
}

void call_base_print(shared_ptr<Base> a) {
  a->print();
}

int main() {
  shared_ptr<GrandChild> gchild(new GrandChild);
  call_print(shared_ptr<Child<int> >(gchild));
  // call_print(gchild);  // Cannot compile!
  call_base_print(gchild);  // This works.
  return 0;
}

I found it strange that call_base_print(gchild) works but call_print(gchild) causes a compiler error. I know that C++ does not allow two implicit conversions, but I don't think there're two conversions here... Could anyone enlighten me?

Hong Jiang
  • 2,260
  • 1
  • 13
  • 13
  • 1
    while MSalters correctly points out this is an instantiation problem, and not a conversion problem, I think it's worth pointing out for arguments used in template deduction, there's not even one implicit conversion allowed, see my anwer to http://stackoverflow.com/questions/867462/c-implicit-conversions/868398#868398 for more details – Pieter Sep 02 '09 at 11:23

2 Answers2

5

You don't get to the point of type conversion. It fails earlier, in template instantiation.

call_base_print doesn't require type deduction. call_print<T>(shared_ptr<Child<T> > a) does. You're passing a shared_ptr<GrandChild>. And there's simply no T you can substitute such that shared_ptr<Child<T> > is shared_ptr<GrandChild>. Therefore, instantiation fails, and there is no function to call.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

You expect that the shared_ptr<GrandChild> is derived from shared_ptr<Child>, only then can an implicit conversion be done. It isn't; it's a different type. So call_print expects another type of argument than you provide.

Another warning: even if there were a conversion from subclass to superclass, it would incur slicing of the subclass object, since you provide the arguments by value.

You can get the behavior you aim for by using pointer arguments i.s.o. shared_ptrs.

xtofl
  • 40,723
  • 12
  • 105
  • 192
  • Actually, you can't get the behavior you want with dumb pointers either, because you would then do Template Argument Deduction on a `Child*` parameter and a `GrandChild*` actual argument. That still fails to deduce T. – MSalters Sep 02 '09 at 10:35