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 ответа:
Ответ по @AlexanderGessler является неполным в нескольких деталях. Итак, давайте поиграем в компилятор для обоих выражений и обоих типов, не так ли?
Выражение 1
cout << (v1 == v2) << endl;
Во-первых, для обоих
Во-вторых, зависимый от аргумента поиск имени (ADL) находит шаблон функцииtype1
иtype2
, поиск неполного имени начинается с области функцииmain()
наружу и находит вашу собственную функциюoperator==
в глобальной области.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. Он принимает предикат, поэтому вы можете просто определить свой компаратор как обычную функцию/функтор и передать его.