mfontanini mfontanini - 25 days ago 14
C++ Question

Ambiguous call to std/boost move

Came across this code which doesn't compile:

#include <boost/move/utility.hpp>
#include <utility>
#include <deque>
#include <map>
#include <vector>
#include <boost/date_time/posix_time/posix_time_types.hpp>

using namespace std;

int main() {
typedef std::pair<int, std::deque<int>> FirstPair;
typedef std::vector<FirstPair> VectorFirstPair;
typedef std::pair<boost::posix_time::time_duration, VectorFirstPair> SecondPair;
typedef std::map<boost::posix_time::time_duration, SecondPair> Map;
Map mapInstance;
SecondPair newElement = make_pair(boost::posix_time::not_a_date_time, VectorFirstPair());
mapInstance.insert(make_pair(boost::posix_time::seconds(10), move(newElement))).first;
}


This fails on gcc 4.8.2 using boost 1.55 (not on boost 1.54) with the following error (ideone here):

test.cpp: In function ‘int main()’:
test.cpp:17:81: error: call of overloaded ‘move(SecondPair&)’ is ambiguous
mapInstance.insert(make_pair(boost::posix_time::seconds(10), move(newElement))).first;
^
test.cpp:17:81: note: candidates are:
In file included from /usr/include/c++/4.8/bits/stl_pair.h:59:0,
from /usr/include/c++/4.8/utility:70,
from /usr/include/boost/config/no_tr1/utility.hpp:21,
from /usr/include/boost/config/select_stdlib_config.hpp:37,
from /usr/include/boost/config.hpp:40,
from /usr/include/boost/move/detail/config_begin.hpp:10,
from /usr/include/boost/move/utility.hpp:17,
from test.cpp:1:
/usr/include/c++/4.8/bits/move.h:101:5: note: constexpr typename std::remove_reference< <template-parameter-1-1> >::type&& std::move(_Tp&&) [with _Tp = std::pair<boost::posix_time::time_duration, std::vector<std::pair<int, std::deque<int> > > >&; typename std::remove_reference< <template-parameter-1-1> >::type = std::pair<boost::posix_time::time_duration, std::vector<std::pair<int, std::deque<int> > > >]
move(_Tp&& __t) noexcept
^
In file included from test.cpp:1:0:
/usr/include/boost/move/utility.hpp:138:55: note: typename boost::remove_reference<T>::type&& boost::move(T&&) [with T = std::pair<boost::posix_time::time_duration, std::vector<std::pair<int, std::deque<int> > > >&; typename boost::remove_reference<T>::type = std::pair<boost::posix_time::time_duration, std::vector<std::pair<int, std::deque<int> > > >]
inline typename remove_reference<T>::type && move(T&& t) BOOST_NOEXCEPT


Shouldn't this compile? Shouldn't the
using namespace
clause make this unambiguous? Why is the compiler choosing
boost::move
as a viable candidate here?

Note that this doesn't cause any errors if I remove the
boost
types in the types defined (replacing them with e.g.
int
).

Answer

This is because of ADL - argument dependant lookup (see this answer which is also boost related).

The problem is that the namespace boost is considered because:

§ 3.4.2 Argument-dependent name lookup

  1. ... The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). ...

Thus, because some boost-related type is a template argument of std::pair, boost::move is also considered. (imho it shouldn't because I can't figure out how to program around that ambiguity).

Comments