Неоднозначная перегрузка для оператора '==' с собственным классом и std :: string_view - PullRequest
2 голосов
/ 15 января 2020

Я создал класс с именем MyClass в namespace N. Теперь я определяю глобальный operator== в 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));
}
}

. Это приводит к тому, что другой код не может быть скомпилирован с ошибкой (в том числе и из-за пропуска):

ошибка : неоднозначная перегрузка для 'operator ==' (типы операндов: 'std :: basic_string_view' и 'const char [3]')

примечание: кандидат: 'bool N :: operator == (const N: : MyClass &, const T &) [с T = char [3]] '

примечание: кандидат:' constexpr bool std :: operator == (std :: basic_string_view <_CharT, _Traits>, std :: __ detail :: __ idt>) [с _CharT = char; _Traits = std :: char_traits; std :: __ detail :: __ idt> = std :: basic_string_view] '479 |
operator == (basic_string_view <_CharT, _Traits> __x

Почему N::operator== даже считается?

-

Редактировать первый код, который обнаружил проблему, был

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

-

Минимальный рабочий пример:

#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";
        }
}

Компилировать с примером

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

1 Ответ

2 голосов
/ 15 января 2020

MyClass имеет конструктор шаблонов, который работает с std::string_view.

Поскольку конструктор не помечен explicit, компилятору разрешено использовать его для неявных преобразований.

Это означает, что когда вы делаете

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

, компилятору разрешается конвертировать a_thing.substr(0,2) в MyClass, а затем его можно использовать для вызова вашего bool operator==.

Один из способов избежать чего-либо вот так, чтобы сделать конструктор MyClass явным.

template<typename T,
    typename = std::void_t<decltype(T{}.data())>,
    typename = std::void_t<decltype(T{}.size())>>
    explicit MyClass(const T& a_source)
    {}
...