cannot resolve shared_ptr calls

I don't understand why the following program doesn't compile when using smart_ptr but works fine with raw pointers: #include <boost/shared_ptr.hpp> class Base { public: virtual ~Base() {}; }; class Base1 : public Base { public: virtual ~Base1() {}; }; class Base2 : public Base { public: virtual ~Base2() {}; }; class Derived1 : public Base1 {}; class Derived2 : public Base2 {}; typedef boost::shared_ptr<Base> BasePtr; typedef boost::shared_ptr<Base1> Base1Ptr; typedef boost::shared_ptr<Base2> Base2Ptr; typedef boost::shared_ptr<Derived1> Derived1Ptr; typedef boost::shared_ptr<Derived2> Derived2Ptr; void f(BasePtr ptr) {} void f(Base1Ptr ptr) {} void f(Base2Ptr ptr) {} int main() { Derived1Ptr d1(new Derived1); Derived2Ptr d2(new Derived2); f(d1); f(d2); } I see the following compile errors with GCC 3.4.4: smart-test.cpp: In function `int main()': smart-test.cpp:24: error: call of overloaded `f(Derived1Ptr&)' is ambiguous smart-test.cpp:15: note: candidates are: void f(BasePtr) smart-test.cpp:16: note: void f(Base1Ptr) smart-test.cpp:17: note: void f(Base2Ptr) smart-test.cpp:25: error: call of overloaded `f(Derived2Ptr&)' is ambiguous smart-test.cpp:15: note: candidates are: void f(BasePtr) smart-test.cpp:16: note: void f(Base1Ptr) smart-test.cpp:17: note: void f(Base2Ptr) If I remove the extra layer of inheritance it works as it should. #include <boost/shared_ptr.hpp> class Base { public: virtual ~Base() {}; }; class Derived1 : public Base {}; class Derived2 : public Base {}; typedef boost::shared_ptr<Base> BasePtr; typedef boost::shared_ptr<Derived1> Derived1Ptr; typedef boost::shared_ptr<Derived2> Derived2Ptr; void f(BasePtr ptr) {} int main() { Derived1Ptr d1(new Derived1); Derived2Ptr d2(new Derived2); f(d1); f(d2); } As I said earlier, doing the same with raw pointers resolves things as would be expected in both cases. Any ideas? --Steven

Steven Solie wrote:
I don't understand why the following program doesn't compile when using smart_ptr but works fine with raw pointers:
The reason is in the overloading resolution rules. Simplified: class C : class B : class A (A is the ultimate base) f(A*), f(B*) f(new C) -> call f with C*. The ideal match has a C* parameter, that doesn't exist. The candidates are A* and B*. In the same inheritance hierarchy, the most derived class wins, thus the B* call is chosen. f(smartptr<A>), f(smartptr<B>) However, smart pointers can't be just cast to smart pointers to base classes by the language, this is done using a cast operator: template<typename Target> operator smartptr<Target>() { return smartptr<Target>(myptr); } This is the typical implementation of such an operator. If myptr is convertible to Target*, the call will succeed. If not, an attempt to instantiate the operator will fail (but overload resolution that attempts to instantiate won't cause a build error due to SFINAE). smartptr<C> thus can generate two valid operators: operator smartptr<A> and operator smartptr<B>. But as far as the language is concerned, these are no different from, say, operator int. They have equal rights, they aren't part of an inheritance hierarchy. For this reason, f(smartptr<A>) and f(smartptr<B>) are equally possible as overload choices, and the compiler stops with an ambiguity error. Sebastian Redl
participants (2)
-
Sebastian Redl
-
Steven Solie