On Friday 12 July 2013 10:41:09 Pieter wrote:
LS,
I have noticed that using Boost.Operators has a side-effect which I can't explain. I am using std::make_shared as a default, however, some of the boost libraries I use pull in boost::make_shared as well.
When using some class T2 derived from one of the boost.operators classes, this causes MSVC2010 to not compile when invoking make_shared<T2> as the compiler can no longer resolve between the two overloaded versions of make_shared for some reason.
I am hoping that someone out can explain what is happening here. A minimal example is included here:
#include <memory>
#include
#include
struct T1 {};
struct T2 : boost::equality_comparable<T2> {};
int main( int argc, const char* argv[] )
{
T1 Object1;
T2 Object2;
using std::make_shared;
std::shared_ptr<T1> Ptr1 = make_shared<T1>(Object1); // OK
std::shared_ptr<T2> Ptr2 = make_shared<T2>(Object2); // error C2668: 'boost::make_shared' : ambiguous call to overloaded function
return 1;
}
This happens because of the argument dependent lookup (ADL) rules. When you invoke unqualified make_shared, the compiler tries to find the appropriate function. You have imported std::make_shared to the function scope, so this one is always found. In the first call you specify Object1 of type T1 as the argument, and T1 is in the global namespace and does not have any other associated types with it. Therefore the imported std::make_shared is the only function the compiler finds, so no ambiguity in this case. In the second case you pass Object2 of type T2. T2 is also defined in the global namespace, which has still has no make_shared functions. But the compiler goes further and checks other types associated with T2 - in this case, its base class boost::equality_comparable, which resides in namespace boost. Through this base class the compiler discovers boost::make_shared, which makes the call ambiguous. The described behavior is expected and standard. However, I think this is a flaw in Boost.Operators. Since these classes are intended to be base classes for user-defined types, I think they should attempt to prevent from messing ADL up like in this example. The solution is rather simple - all the base classes should be enveloped into an inner namespace (e.g. boost::operators) and then imported to namespace boost for backward compatibility. This way ADL will look for make_shared in boost::operators and will not find it there. I suggest you to create a ticket for Boost.Operators to implement that change.