find () с помощью перегруженного оператора==


Я пытаюсь найти элемент в векторе, используя перегруженный оператор==(). Однако, если использовать type1 в следующем коде, выход будет равен 1 и 0 (не найден). Использование type2 дает как 1, так и 1. Среда-Xubuntu 12.04 и g++ версии 4.6.3.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<string, int> type1;
struct type2: public type1 {};
#define TYPE type1

bool operator== (const TYPE& lhs, const TYPE& rhs) {
    return lhs.first == rhs.first;
}

int main()
{
    vector<TYPE> vec;
    TYPE v1, v2;

    v1.first = "abc"; v1.second = 1; vec.push_back(v1);
    v2.first = "abc"; v2.second = 2;

    cout << (v1 == v2) << endl;
    cout << (find(vec.begin(), vec.end(), v2) != vec.end()) << endl;
}
4 8

4 ответа:

Ответ по @AlexanderGessler является неполным в нескольких деталях. Итак, давайте поиграем в компилятор для обоих выражений и обоих типов, не так ли?

Выражение 1

cout << (v1 == v2) << endl;

Во-первых, для обоих type1 и type2, поиск неполного имени начинается с области функции main() наружу и находит вашу собственную функцию operator== в глобальной области.

Во-вторых, зависимый от аргумента поиск имени (ADL) находит шаблон функции operator== для std::pair из namespace std. На самом деле, ADL находит гораздо больше шаблонов функций std::operator== (те из std::vector и std::string, так как вы также включили эти заголовки).

Примечание : ADL также находит соответствие для type2, поскольку его базовый класс type1 добавит namespace std к множеству связанных с ним пространств имен.


3.4.2 поиск имени, зависящего от аргумента [basic.уважать.аргдеп]

- Если T-тип класса (включая союзы), то его ассоциированные классы являются: класс сам по себе; класс который он является членом, если таковой имеется; и его прямые и косвенные базовые классы. Связанные пространства имен являются пространства имен, членами которых являются связанные с ними классы.


В-третьих, вывод аргумента шаблона происходит для всех найденных шаблонов функций. Для type1 только шаблон функции для std::pair выдержит дедукцию аргументов (и он выводит свои аргументы шаблона, чтобы быть std::string и int соответственно). Однако для type2 не существует набора шаблонов аргументы, которые подойдут, потому что type2 не является экземпляром шаблона std::pair.

В-четвертых, разрешение перегрузки вступает в игру. Для type1, Как ваша собственная функция operator==, так и шаблон функции std::operator== имеют одинаковый ранг (точное соответствие). Поэтому тай-брейк выберет вашу не шаблонную функцию. Для type2 существует только одна жизнеспособная функция, поэтому разрешение перегрузки не вступает в игру, и ваша функция будет выбрана.

Заключение 1: type1 и type2 даст тот же ответ (ваша версия выбрана), хотя и по разным причинам.

Выражение 2

cout << (find(vec.begin(), vec.end(), v2) != vec.end()) << endl;

Здесь мы должны сначала Разрешить вызов find. Из-за вашего using namespace std;, неполный поиск имени уже находит (без каламбура) std::find, но даже без директивы using, ADL на итераторе std::vector нашел бы его. Он выведет третий аргумент шаблона для std::find либо type1, либо type2.

Внутри std::find, вызов к operator== найден. Опять же, сначала будет выполнен обычный поиск. Однако это происходит изнутри namespace std. Он найдет несколько шаблонов функций operator== (для std::vector, std::string и std::pair). Как только кандидаты в одной области будут найдены во время поиска неквалифицированного имени, эта фаза поиска имени прекращается.

Однако АДЛ по-прежнему выполняется. Обратите внимание, что глобальное пространство имен не является связанным пространством имен для type1 потому что это только typedef для класса в namespace std. Так что за type1, ADL не находит ничего нового. В отличие от этого, type2 имеет глобальное пространство имен в качестве связанного пространства имен, и поэтому ADL найдет ваш шаблон функции operator== в этом случае.

Для type1, шаблон-аргумент-дедукция находит std::string и int в качестве аргументов шаблона для operator== шаблона функции для std::pair. Для type2 снова нет набора аргументов шаблона, который будет соответствовать, потому что type2 не является экземпляром шаблона std::pair.

Это оставляет разрешение перегрузки. Для type1 существует только одна жизнеспособная функция (экземпляр шаблона std::operator==), и разрешение перегрузок не вступает в игру. Для type2 Существует также только одна жизнеспособная функция (жизнеспособная, потому что она требует только стандартного преобразования derived-to-base). Следовательно, разрешение перегрузки также не вступает в игру.

Вывод 2 : для type1 (std версия) и type2 (ваша версия) вы получаете разные результаты.

Резюме

Просто потому, что эти вещи могут стать очень сложно с несколькими перегрузками в разных пространствах имен, вот сводная таблица со Святой Троицей (поиск имени, вывод аргументов и разрешение перегрузки). Для каждой фазы и для каждого типа я перечислил выживших кандидатов после этой фазы. В нижней строке показана вызываемая функция.

Выражение 1

+---------------------+-----------------+-----------------+
| phase               | type1           | type2           |
+---------------------+-----------------+-----------------+
| unqualified lookup  |    ::operator== |    ::operator== |
| ADL                 | std::operator== | std::operator== |
+---------------------+-----------------+-----------------+
| argument deduction  |    ::operator== |    ::operator== |
|                     | std::operator== |                 |
+---------------------+-----------------+-----------------+
| overload resolution |    ::operator== |    ::operator== |
+---------------------+-----------------+-----------------+

Выражение 2

+---------------------+-----------------+-----------------+
| phase               | type1           | type2           |
+---------------------+-----------------+-----------------+
| unqualified lookup  | std::operator== | std::operator== |
| ADL                 |                 |    ::operator== |
+---------------------+-----------------+-----------------+
| argument deduction  | std::operator== |    ::operator== |
+---------------------+-----------------+-----------------+
| overload resolution | std::operator== |    ::operator== |
+---------------------+-----------------+-----------------+

Обратите внимание, что неквалифицированный поиск находит другое имя в зависимости от области, в которой он начинается (функция scope inside global scope versus namespace scope), и что ADL аналогичным образом находит другое имя в зависимости от того, какое пространство имен считается связанным (namespace std vs The global namespace).

std::pair имеет свое значение по умолчанию operator== в пространстве имен std, которое является шаблоном для произвольных пар, сравнивая поля first и second. Этот оператор выбирается в одном из четырех случаев, а именно find с TYPE == type1. Детали немного сложны, хотя:

Что на самом деле происходит для TYPE == type1, это (поправьте меня, если я ошибаюсь)

  • для v1 == v2 ADL (зависимый от аргумента поиск имени) применяется для поиска operator== в std, то есть этот оператор добавляется к нормальная перегрузка установлена. Однако версия без шаблона в текущей единице перевода по-прежнему предпочтительнее шаблона operator== из std.
  • вызов std::find выполняется в std, таким образом, Поиск operator== начинается непосредственно в std. Он находит один матч (без использования ADL!) и поэтому не ищет охватывающую область, которая содержала бы собственный оператор ОП.

И для TYPE == type2

  • v1 == v2 легко-он непосредственно находит operator== в пространства имен.
  • std::find также создается в std, но пользовательский оператор из основной области добавляется к набору разрешения перегрузки с помощью ADL, а затем оказывается более специфичным, чем в std.

std::pair имеет свой собственный operator== это имеет приоритет над вашим собственным.

Я думаю, что вам лучше использовать find_if вместо find. Он принимает предикат, поэтому вы можете просто определить свой компаратор как обычную функцию/функтор и передать его.