Search code examples
c++clanglibc++

'use of overloaded operator is ambiguous' when using reverse vector iterators in Clang with libc++


As far as I understand C++ ADL this compilation error is legit. But at the same time I dont understand why this failure happens only on clang + libc++ configuration, and only when we use reverse iterators. Normal vector iterators dont lead to this.

#include <vector>
#include <iostream>

namespace NS1
{
struct Foo
{
    explicit Foo(const int i): m_i(i)
    {}
    bool operator==(const Foo& that) const
    {
        return this->m_i == that.m_i;
    }

    int m_i;
};

template <typename T>
bool operator!=(const T& a, const T& b)
{
    return !(a == b);
}
}

int main(void)
{
    std::vector<NS1::Foo> n;
    // error: use of overloaded operator '!=' is ambiguous (with operand types 'std::__1::reverse_iterator
    for(auto it = n.rbegin(); it != n.rend(); ++it)
    {
        std::cout<<it->m_i;
    }
}

The error is:

error: use of overloaded operator '!=' is ambiguous (with operand types 'std::__1::reverse_iterator<std::__1::__wrap_iter<NS1::Foo *>>' and 'std::__1::vector<NS1::Foo, std::__1::allocator<NS1::Foo>>::reverse_iterator' (aka 'reverse_iterator<__wrap_iter<NS1::Foo *>>'))
for(auto it = n.rbegin(); it != n.rend(); ++it)
                          ~~ ^  ~~~~~~~~

Does anyone know if it is possible to make clang + libc++ configuration behave like the rest of configurations (gcc, msvc)?

Here is a small example on godbolt: https://godbolt.org/z/8cKPY6


Solution

  • As far as I understand C++ ADL this compilation error is legit. But at the same time I dont understand why this failure happens only on clang + libc++ configuration, and only when we use reverse iterators. Normal vector iterators dont lead to this

    Well, if one examines the error message, one sees the actual types of the operands are some variant of this

    std::__1::reverse_iterator<std::__1::__wrap_iter<NS1::Foo *>>
    

    It's an instance of some template that is used to generate the reverse iterators. Since you understand ADL, you know that the set of associated namespaces is composed also of the namespaces of template arguments, when an operand is a class template specialization. This is what pulls your NS1::operator!= into consideration. Since it's an unconstrained function template, it could be a viable candidate for overload resolution. Hence the conflict.

    The reason is doesn't occur with forward iterators is likely subject to their implementation details. They could

    1. Not be a specialization of some template themselves. I.e, regular nested classes of a vector specialization. The associated namespaces of the vector don't carry over.
    2. Not even be classes at all. A vector's iterator can be implemented as a simple pointer into the vector's buffer.

    I'd seriously re-examine your need for such an unconstrained operator!=. That would be a way to deal with the problem that is pretty straight-forward.