Я реализовал свою перегрузку operator==
для сравнения своего std::pair<...>
с std::string
. Но по какой-то причине компилятор не может найти эту перегрузку. С чем это может быть связано?
Код для воспроизведения ошибки:
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
typedef std::pair<std::string, int> RegPair;
bool operator==(const RegPair& lhs, const std::string& rhs)
{
return lhs.first == rhs;
}
int main()
{
std::vector<RegPair> sequence;
std::string foo("foo");
std::find(sequence.begin(), sequence.end(), foo);
}
Текст ошибки:
GNU GCC:
error: no match for 'operator==' in '__first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* with _Iterator = std::pair, std::allocator >, int>*, _Container = std::vector, std::allocator >, int>, std::allocator, std::allocator >, int> > > == __val'
clang:
error: invalid operands to binary expression ('std::pair, int>' and 'std::basic_string const')
Данный вопрос является свободным переводом «Why isn't std::find() using my operator==?».
Ответ по ссылке, с которой был сделан перевод, неверен/неточен. ADL-поиск никак не заменяет/не исключает обычный поиск, а лишь дополняет его.
Правильное описание ситуации заключается в следующем:
Обычный поиск выполняется из места вызова оператора ==
из определения шаблона функции std::find
в стандартной библиотеке. Он находит только те имена, которые видны из этого места.
Понятно, что оттуда приведенное определение оператора ==
не видно.
ADL-поиск выполняется в ассоциированных и только в ассоциированных пространствах имен и видит эти пространства имен такими, каким они стали на момент вызова функции std::find
в вызывающем коде. Набор ассоциированных пространств имен строится в соответствии с правилами, описанными 6.4.2/2.
В данном случае из точки вызова std::find
приведенное определение оператора ==
прекрасно видно. Но это определение сделано в глобальном пространстве имен. А ассоциированным для ADL в данном случае является только пространство std
, ибо оба аргумента сравнения принадлежат пространству std
. Поэтому глобальное пространство имен не рассматривается ADL и данное определение не находится.
Утверждение о том, что ADL якобы прекращает дальнейший поиск определений operator ==
именно из-за того, что какие-то определения operator ==
уже найдены внутри std
- неверно. ADL всегда ищет имена только внутри ассоциированных пространств имен. В отличие от обычного lookup, ADL никогда не расширяет область поиска за пределы ассоциированных пространств имен, независимо от того, найдено там что-либо или нет.
Ту же проблему можно проиллюстрировать следующим маленьким примером
namespace N
{
struct S {};
}
template <typename T> void foo(T a)
{
bar(a); // 1
}
void bar(N::S s) {}
int main()
{
N::S a;
foo(a); // 2
}
При таком порядке объявлений обычный поиск имен находит имена, видные из точки 1, а ADL поиск находит имена, видные из точки 2, но только в ассоциированных пространствах имен. Глобальное пространство имен ассоциированным не является, поэтому объявление void bar(N::S s)
не находится и код не компилируется.
В исходном варианте, если мы каким-то образом "притянем за уши" глобальное пространство имен в качестве ассоциированного для ADL, то данный оператор ==
сразу начнет находиться через ADL. Например, объявим в глобальном пространстве имен некий фиктивный тип, приводимый к std::string
и используем именно его в качестве ключа для поиска
...
struct S : std::string
{
using std::string::string;
};
int main()
{
std::vector<RegPair> sequence;
S foo("foo");
std::find(sequence.begin(), sequence.end(), foo);
}
Определение оператора сравнения при этом менять не надо. Код сразу начнет компилироваться и использовать данный оператор сравнения.
Другой вариант внешне "невинной" замены, который заставит код компилироваться - сделать второй член пары типом из глобального пространства имен
struct X {};
typedef std::pair<std::string, X> RegPair;
Больше ничего менять не надо - этого уже достаточно для того, чтобы ассоциировать глобальное пространство имен для ADL.
Проблема заключается в том, что std::find
является шаблонной функцией, а потому при поиске operator==
в дело вступает ADL (поиск, зависимый от типов аргументов).
Оба аргумента функции (std::pair
и std::string
) находятся в одном и том же пространстве имён (::std
), поэтому ADL начинает поиск именно в нём. При этом достаточно, чтобы там был определён хоть какой-то operator==
, так как сопоставление имён (name lookup) производится до определения подходящих перегрузок.
В связи с тем, что подходящий по имени operator==
обязательно будет найден (в том же<string>
объявлен как минимум оператор сравнения std::string
с чем-то), алгоритм останавливает свою работу. До вашей же перегрузки, расположенной в глобальном пространстве имён (то есть за пределами ::std
), очередь так и не дойдёт.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Тут вроде все норм, а снизу неочень
Очень долго старался понять в чем же ошибка, когда я использую std::bind2nd
Всем привет, мне нужно узнать программно где лежат файл программы