Я пытаюсь найти элемент в векторе, используя перегруженный оператор == (). Однако при использовании 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;
}
Ответ @AlexanderGessler неполный в нескольких деталях. Итак, давайте играть компилятором для обоих выражений и обоих типов, не так ли?
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.lookup.argdep]
— Если T является типом класса (включая объединения), его ассоциированные классы:
сам класс; класс, членом которого он является, если таковой имеется; И его
прямые и косвенные базовые классы. Его связанные пространства имен являются
пространства имен, членами которых являются связанные с ним классы.
В третьих, вычет аргумента шаблона происходит для всех найденных шаблонов функций. За type1
, только шаблон функции для std::pair
переживет вычет аргумента (и выводит свои аргументы шаблона, чтобы быть std::string
а также int
соответственно). Однако для type2
, нет набора шаблонных аргументов, которые будут соответствовать type2
это не экземпляр std::pair
шаблон.
В-четвертых, разрешение перегрузки вступает в игру. За type1
обе ваши собственные функции operator==
и std::operator==
Шаблон функции имеет одинаковый ранг (точное совпадение). Поэтому тай-брейк выберет вашу не шаблонную функцию. За type2
, есть только одна жизнеспособная функция, поэтому разрешение перегрузки не вступает в игру, и ваша функция будет выбрана.
Вывод 1: type1
а также type2
даст тот же ответ (ваша версия выбрана), хотя и по разным причинам.
cout << (find(vec.begin(), vec.end(), v2) != vec.end()) << endl;
Здесь нам нужно сначала разрешить звонок find
, Из-за вашего using namespace std;
Поиск безусловного имени уже найден (каламбур не предназначен) std::find
, но даже без использования директивы ADL на std::vector
Итератор нашел бы это. Это выведет третий аргумент шаблона для std::find
либо type1
или же type2
,
В std::find
вызов operator==
найден. Опять же, обычный поиск будет выполняться первым. Однако это происходит изнутри namespace std
, Найдет несколько operator==
шаблоны функций (для std::vector
, std::string
а также std::pair
). Как только кандидаты в одной области обнаруживаются при поиске без оговорок, эта фаза поиска имен прекращается.
Однако ADL все еще выполняется. Обратите внимание, что глобальное пространство имен не является связанным пространством имен для 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== |
+---------------------+-----------------+-----------------+
Обратите внимание, что неквалифицированный поиск находит другое имя в зависимости от области, в которой он запускается (область функции внутри глобальной области по сравнению с областью пространства имен), и что ADL аналогичным образом находит другое имя в зависимости от того, какое пространство имен считается связанным (namespace std
против глобального пространства имен).
std::pair
имеет значение по умолчанию operator==
в пространстве имен std
, который является шаблоном для произвольных пар, сравнивая оба first
и second
поля. Этот оператор выбирается в одном из четырех случаев, а именно find
с TYPE == type1
, Детали немного сложнее, хотя:
Что на самом деле происходит для TYPE == type1
есть (поправьте меня, если я ошибаюсь)
v1 == v2
ADL (Аргумент зависимый поиск имени) применяется, чтобы найти operator==
в std
Это означает, что этот оператор добавляется к обычному набору перегрузки. Однако версия без шаблона в текущем модуле перевода по-прежнему предпочтительнее шаблона operator==
от std
,std::find
вызов происходит в течение std
таким образом ищите operator==
начинается прямо в std
, Он находит одно совпадение (без использования ADL!) И, следовательно, не ищет вмещающую область, которая содержала бы собственный оператор OP.И для TYPE == type2
v1 == v2
легко — он непосредственно находит operator==
во вложенном пространстве имен.std::find
также экземпляры в std
, но пользовательский оператор из основной области действия добавляется в набор разрешений перегрузки с использованием ADL, а затем оказывается более конкретным, чем оператор в std
,std::pair
имеет свой operator==
это имеет приоритет над вашим собственным.
Я думаю, что вам лучше использовать find_if вместо того, чтобы найти. Он принимает предикат, поэтому вы можете просто определить свой компаратор как обычную функцию / функтор и передать его.