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 stdvs The global namespace).
std::pairимеет свое значение по умолчаниюoperator==в пространстве именstd, которое является шаблоном для произвольных пар, сравнивая поляfirstиsecond. Этот оператор выбирается в одном из четырех случаев, а именноfindсTYPE == type1. Детали немного сложны, хотя:Что на самом деле происходит для
TYPE == type1, это (поправьте меня, если я ошибаюсь)
- для
v1 == v2ADL (зависимый от аргумента поиск имени) применяется для поиска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. Он принимает предикат, поэтому вы можете просто определить свой компаратор как обычную функцию/функтор и передать его.