Sometimes, C++’s overload resolution rules are a pain in the ass.
Let’s say we have the following:
#include <tr1/memory> struct Bar { Bar() { } }; struct Baz { Baz() { } }; struct Foo { explicit Foo(const std::tr1::shared_ptr<const Bar> &) { } explicit Foo(const std::tr1::shared_ptr<const Baz> &) { } };
Then the following is ambiguous, and won’t compile:
Foo foo(std::tr1::shared_ptr<Bar>(new Bar));
Here’s why: Neither constructor exactly matches the argument given, so the compiler falls back to construction and type conversions. std::tr1::shared_ptr<T_> has an implicit constructor template <typename U_> shared_ptr(const shared_ptr<U_> &), which is good because it lets you use a shared pointer to a derived class when a shared pointer to a base class is expected. But that conversion can take place for all U_, which means the compiler doesn’t know whether you want to convert to a shared pointer to const Bar or const Baz — it isn’t until the constructor body is instantiated that the compiler finds that only one of the two conversions will compile successfully.
So, one has to be explicit when creating the shared pointer:
Foo foo(std::tr1::shared_ptr<const Bar>(new Bar));
Except, usually we create shared pointers using a helper function, to avoid specifying the type name twice:
template <typename T_> std::tr1::shared_ptr<T_> make_shared_ptr(T_ * const t) { return std::tr1::shared_ptr<T_>(t); }
So we’re stuck having to use a slightly weird looking allocation:
Foo foo(make_shared_ptr(new const Bar));
Incidentally, C++0x has a std::make_shared which is a lot better than this, but it requires rvalue references and std::forward to work. It would look like this:
Foo foo(std::make_shared<Bar>());
Or, if we still need the const:
Foo foo(std::make_shared<const Bar>());
Why might we not need the const? The current C++0x draft standard includes the wording “[the template] constructor shall not participate in the overload resolution unless U_ * is implicitly convertible to T_ *“, which presumably means implementations have to solve the problem using concepts to restrict the template constructor.
And, of course, there’s one final gotcha. The new const Foo form is only legal if Foo has a user defined constructor. I have no idea why, but C++03 explicitly says so.