-->

error: call of overloaded distance is ambiguous

2019-06-12 15:30发布

问题:

I have a bit of code (which I didn't write, but am trying to compile) -- iostream_combo.cc--, and doing so gives me the following error:

./moses/moses/comboreduct/combo/iostream_combo.cc: In function ‘std::__cxx11::string opencog::combo::l2ph(const string&, const std::vector<std::__cxx11::basic_string<char> >&)’:
./moses/moses/comboreduct/combo/iostream_combo.cc:543:64: error: call of overloaded ‘distance(std::vector<std::__cxx11::basic_string<char> >::const_iterator, __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >&)’ is ambiguous
             arity_t idx = distance(labels.begin(), found_it) + 1;
                                                            ^ In file included from /usr/include/c++/8/bits/stl_algobase.h:66,
             from /usr/include/c++/8/bits/char_traits.h:39,
             from /usr/include/c++/8/ios:40,
             from /usr/include/c++/8/ostream:38,
             from /usr/include/c++/8/iostream:39,
             from ./moses/moses/comboreduct/combo/iostream_combo.h:28,
             from ./moses/moses/comboreduct/combo/iostream_combo.cc:24:
/usr/include/c++/8/bits/stl_iterator_base_funcs.h:138:5: note: candidate: ‘typename std::iterator_traits<_Iterator>::difference_type std::distance(_InputIterator, _InputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; typename std::iterator_traits<_Iterator>::difference_type = long int]’
 distance(_InputIterator __first, _InputIterator __last)
 ^~~~~~~~ In file included from /usr/local/include/boost/range/distance.hpp:18,
             from /usr/local/include/boost/range/functions.hpp:21,
             from /usr/local/include/boost/range/iterator_range_core.hpp:38,
             from /usr/local/include/boost/lexical_cast.hpp:30,
             from ./moses/moses/comboreduct/combo/iostream_combo.h:30,
             from ./moses/moses/comboreduct/combo/iostream_combo.cc:24:
/usr/local/include/boost/iterator/distance.hpp:49:9: note: candidate: ‘constexpr typename boost::iterators::iterator_difference<Iterator>::type boost::iterators::distance_adl_barrier::distance(SinglePassIterator, SinglePassIterator) [with SinglePassIterator = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; typename boost::iterators::iterator_difference<Iterator>::type = long int]’
     distance(SinglePassIterator first, SinglePassIterator last)
     ^~~~~~~~

I'm using Ubuntu 16.04 x64, Boost 1.68 and gcc 8.2. So, the steps to reproduce the problem would be as follows:

  1. On Ubuntu 16.04
  2. install gcc-8
  3. Use it to build boost 1.68 from source
  4. Git clone the moses repository sand follow the instructions from there: basically 1) git clone and build cogutil; 2) try to make moses: cd build, cmake .., make.

I understand C++ enough (I think) that I can see that the call to std::distance is overloaded. What I don't see is the way to disambiguate it, although I guess it must include some re-writing of found_it or some explicit castings instead of auto.

回答1:

The calls are like:

    arity_t idx = distance(labels.begin(), found_it) + 1;

This means that distance is found via ADL and hence all associated namespaces are considered. This might prove problematic if e.g. there are two namespaces providing an equally applicable overload for distance:

Live On Coliru

#include <iterator>
#include <vector>

namespace OyVeh {
    struct X { };

    template <typename It>
    size_t distance(It, It) { return 42; } // just to throw in a wrench
}

int main() {
    std::vector<OyVeh::X> v{3};

    auto f = v.begin();
    auto l = v.end();

    // now f and l have both `::std` and `::OyVeh` as associated namespaces. The following is ambiguous:

    return distance(f, l);
}

There are roughly 2 ways to fix it:

  • remove the ambigious distance declaration from the associated namespace (this might not be possible if e.g. the ones competing are std::distance and boost::distance)
  • edit the calls to remove the reliance on ADL (e.g. qualify them like std::distance(...) or parenthesize them (distance)(...))

Showing the workarounds:

Live On Coliru

{
    using OyVeh::distance;
    return (distance)(f, l); // parentheses suppress ADL
}

return std::distance(f, l); // also works, obviously
return OyVeh::distance(f, l); // but beware the meaning might change