2

I created a class named MyClass in namespace N. Now I define a global operator== in namespace N.

namespace N
{
class MyClass
{
// member function begin()
// member function end()
// content omitted
}

template<typename T>
bool operator==(const MyClass& a_mc, const T& a_other)
{
    using namespace std;
    return std::equal(a_mc.begin(), a_mc.end(), begin(a_other));
}
}

This results in other code failing to compile with (included-from-stack omitted):

error: ambiguous overload for 'operator==' (operand types are 'std::basic_string_view' and 'const char [3]')

note: candidate: 'bool N::operator==(const N::MyClass&, const T&) [with T = char [3]]'

note: candidate: 'constexpr bool std::operator==(std::basic_string_view<_CharT, _Traits>, std::__detail::__idt >) [with _CharT = char; _Traits = std::char_traits; std::__detail::__idt > = std::basic_string_view]' 479 |
operator==(basic_string_view<_CharT, _Traits> __x

Why is N::operator== even considered?

--

Edit first code that hit the problem was

bool N::MyClass::f(const std::string_view& a_thing)
{return a_thing.substr(0,2) == "XY";}

--

Minimal working example:

#include <algorithm>
#include <string_view>
#include <type_traits>

namespace N
{
        struct MyClass
        {
                MyClass() noexcept = default;
                template<typename T,
                        typename = std::void_t<decltype(T{}.data())>,
                        typename = std::void_t<decltype(T{}.size())>>
                MyClass(const T& a_source)
                {}

                const char* begin() const { return 0; } // stub
                const char* end() const { return 0; } // stub
                bool f(const std::string_view& a_thing);
        };

        template<typename T>
        bool operator==(const MyClass& a_mc, const T& a_other)
        {
            using namespace std;
            return std::equal(a_mc.begin(), a_mc.end(), begin(a_other));
        }

        bool MyClass::f(const std::string_view& a_thing)
        {
                return a_thing.substr(0,2) == "XY";
        }
}

Compile with

g++ -std=c++17 example.cpp

1 Answers1

2

MyClass has a template constructor that works with std::string_view.

Since the constructor is not marked explicit the compiler is allowed to use it for implicit conversions.

That means that when you do

bool N::MyClass::f(const std::string_view& a_thing)
{return a_thing.substr(0,2) == "XY";}

the compiler is allowed to convert a_thing.substr(0,2) to a MyClass and then it could be used to call your bool operator==.

One way to avoid things like this is to make the constructor of MyClass explicit.

template<typename T,
    typename = std::void_t<decltype(T{}.data())>,
    typename = std::void_t<decltype(T{}.size())>>
    explicit MyClass(const T& a_source)
    {}
super
  • 12,335
  • 2
  • 19
  • 29
  • Why does the error message state that the operand types are 'std::basic_string_view' and 'const char [3]'? – std_unordered_map Jan 15 '20 at 19:08
  • The left-hand-side is a `std::string_view` and the right hand side is `"XY"` which get deduces as `const char [3]` by the template. – super Jan 15 '20 at 19:10
  • @std_unordered_map Another way to put it is that if your `operator==` did not exist `"XY"` would be converted to a `std::string_view`. But since both that and your `operator==` requires 1 conversion to work it is ambiguous which one should be preferred. – super Jan 15 '20 at 19:12