7

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).

mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • 2
    related? http://stackoverflow.com/a/8111892/3102935. It is because of ADL – WorldSEnder Jun 30 '15 at 23:57
  • So ADL uses template parameters too? I thought it only used the top level type (`std::pair` in this case) – mfontanini Jul 01 '15 at 00:00
  • 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)." §3.4.2.2 – WorldSEnder Jul 01 '15 at 00:06
  • Damn, thanks. If you turn that comment into an answer, I'll accept it :). Do you know what's the rationale behind this? I know what ADL is for, but why also take template parameters into account? – mfontanini Jul 01 '15 at 00:11

2 Answers2

5

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).

WorldSEnder
  • 4,875
  • 2
  • 28
  • 64
1

As an alternative to explicitly qualifying std::move everywhere, you can define BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE, which just aliases boost::move to std::move. With that, your example compiles successfully.

dhaffey
  • 1,354
  • 9
  • 12