Search code examples
c++shared-ptroverload-resolution

function overloading ambiguty between bool and boost::shared_ptr<base> when calling with boost::shared_ptr<derived>


#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

struct base {};
struct derived : public base {};

void g(bool b) {}

void g(boost::shared_ptr<base> b) {}

int main()
{
    boost::shared_ptr<base> spbase = boost::make_shared<derived>();
    boost::shared_ptr<derived> spderived = boost::make_shared<derived>();

    g(true); // ok
    g(spbase); //ok
    g(boost::static_pointer_cast<base>(spderived)); // ok
    g(spderived); // I am ambiguous between g(bool b) and g(boost::shared_ptr<base> b).
}

Can someone explain to me why the call to g(spderived) causes a ambiguity between g(bool) and g(boost::shared_ptr)?

Compiling with gcc version 4.6.3 gives following error:

$ g++ shared_test.cpp -I/c/thirdparty/boost_1_55_0/ -o shared_test shared_test.cpp: In function 'int main()': shared_test.cpp:27:13: error: call of overloaded 'g(boost::shared_ptr&)' is ambiguous shared_test.cpp:27:13: note: candidates are: shared_test.cpp:7:6: note: void g(bool) shared_test.cpp:9:6: note: void g(boost::shared_ptr)

Note: If I add -std=c++11 it compiles fine, but I use c++98/c++03, so this doesn't really help me. Clang and VC produce a similar error, compiled under c++03.


Solution

  • In C++03 shared_ptr has implicit bool conversion operator, in C++11 it's explicit. This call is ambigious, since in both cases user-defined conversion will be called, conversion to bool, or conversion to shared_ptr<base>, that is defined like this:

    template<class Y>
    #if !defined( BOOST_SP_NO_SP_CONVERTIBLE )
    
    shared_ptr( shared_ptr<Y> const & r,
    typename boost::detail::sp_enable_if_convertible<Y,T>::type =
    boost::detail::sp_empty() )
    
    #else
    
    shared_ptr( shared_ptr<Y> const & r )
    
    #endif
    

    it's not copy constructor, it's conversion constructor, so chain will be like this

    tmp = shared_ptr<base>(spderived);
    b = shared_ptr<base>(tmp);
    

    there is only one way to do what you want: construct temporary of type shared_ptr<base>.

    g(boost::shared_ptr<base>(spderived));