Search code examples
c++booststloverloadingshared-ptr

C++0x Error: overloading a function with std::shared_ptr to const argument is ambiguous


Suppose I have two unrelated classes A and B. I also have a class Bla that uses boost::shared_ptr like this:

class Bla {
public:
    void foo(boost::shared_ptr<const A>);
    void foo(boost::shared_ptr<const B>);
}

Notice the const. That's the important part which the original version of this question lacked. This compiles, and the following code works:

Bla bla;
boost::shared_ptr<A> a;
bla.foo(a);

However, if I switch from using boost::shared_ptr to using std::shared_ptr in the above examples, I get a compilation error that says:

"error: call of overloaded 'foo(std::shared_ptr<A>)' is ambiguous
note: candidates are: void foo(std::shared_ptr<const A>)
                      void foo(std::shared_ptr<const B>)

Can you help me figure out why the compiler can't figure out which function to use in the std::shared_ptr case, and can in the boost::shared_ptr case? I'm using the default GCC and Boost versions from the Ubuntu 11.04 package repository which are currently GCC 4.5.2 and Boost 1.42.0.

Here is the full code that you can try compiling:

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
// #include <memory>
// using std::shared_ptr;

class A {};
class B {};

class Bla {
public:
    void foo(shared_ptr<const A>) {}
    void foo(shared_ptr<const B>) {}
};

int main() {
    Bla bla;
    shared_ptr<A> a;

    bla.foo(a);

    return 0;
}

By the way, this issue motivated me to ask this question about whether I should be using std::shared_ptr at all yet ;-)


Solution

  • shared_ptr has a template single-argument constructor, which is considered for the conversion here. That's what allows an actual parameter shared_ptr<Derived> to be supplied where a shared_ptr<Base> is needed.

    Since both shared_ptr<const A> and shared_ptr<const B> have this implicit conversion, it's ambiguous.

    At least in C++0x, the standard requires that shared_ptr use some SFINAE tricks to make sure that the template constructor only matches types that actually can be converted.

    The signature is (see section [util.smartptr.shared.const]):

    shared_ptr<T>::shared_ptr(const shared_ptr<T>& r) noexcept;
    template<class Y> shared_ptr<T>::shared_ptr(const shared_ptr<Y>& r) noexcept;
    

    Requires: The second constructor shall not participate in the overload resolution unless Y* is implicitly convertible to T*.

    Possibly the library hasn't yet been updated to comply with that requirement. You might try a newer version of libc++.

    Boost won't work, because it's missing that requirement.

    Here's a simpler test case: http://ideone.com/v4boA (This test case will fail on a conforming compiler, if it compiles successfully, it means the original case will be incorrectly reported as ambiguous.)

    VC++ 2010 gets it right (for std::shared_ptr).