Boost.Operators and make_shared
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
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.
Agreed, boost operators classes should be in boost::operators. I'm working on a patch to fix this issue (have already done so in my test code for operators2 and it works just fine). There's also a macro flag in boost operators BOOST_NO_OPERATORS_IN_NAMESPACE which allows boost operators classes to be in the global namespace. I propose we get rid of this macro and force all operators classes to be in the boost::operators2 namespace.
On Friday 12 July 2013 17:13:39 Andrew Ho wrote:
Agreed, boost operators classes should be in boost::operators. I'm working on a patch to fix this issue (have already done so in my test code for operators2 and it works just fine).
Could we just have boost::operators (i.e. without 2)? The old implementation didn't have a namespace and the new one is supposed to replace it, isn't it?
There's also a macro flag in boost operators BOOST_NO_OPERATORS_IN_NAMESPACE which allows boost operators classes to be in the global namespace. I propose we get rid of this macro and force all operators classes to be in the boost::operators2 namespace.
I don't mind against that. I'm curious though for what compilers this workaround is targeted for. I'm guessing this should be something rather old.
Could we just have boost::operators (i.e. without 2)? The old implementation didn't have a namespace and the new one is supposed to replace it, isn't it?
I'm not entirely sure what the plan is with boost operators2. As far as I
could tell there wasn't much of a consensus on what the plan was (unless I
missed a post somewhere). Ideally it should replace boost operators, but
there are a lot of old compiler fixes which have been removed, and a few
potentially breaking changes in my current test implementation.
Potentially "breaking" changes:
1. there's no "addable1/addable2" class naming, I'm relying on template
partial specialization so addable
On 12/07/13 10:41, 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.
Welcome to ADL. To avoid ADL issues, fully qualify all names.
Welcome to ADL. To avoid ADL issues, fully qualify all names.
Good suggestion, but I still think operators/operators2 should still be in the boost::operators and boost::operators2 namespaces respectively (assuming operators2 is not going to replace operators, otherwise it should go in boost::operators).
participants (4)
-
Andrew Ho
-
Andrey Semashev
-
Mathias Gaunard
-
Pieter