The answer by @AlexanderGessler is incomplete in several details. So let's play compiler for both expressions and both types, shall we?
Expression 1
cout << (v1 == v2) << endl;
First, for both type1
and type2
, unqualified name lookup starts at the main()
function scope outwards, and finds your own operator==
function at the global scope.
Second, argument-dependent name lookup (ADL) finds the function template operator==
for std::pair
from namespace std
. Actually, ADL finds many more std::operator==
function templates (those from std::vector
and std::string
, since you included those headers as well).
Note: ADL also finds a match for type2
, because its base class type1
will add namespace std
to the set of its associated namespaces.
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
— If T is a class type (including unions), its associated classes are:
the class itself; the class of which it is a member, if any; and its
direct and indirect base classes. Its associated namespaces are the
namespaces of which its associated classes are members.
Third, template argument deduction takes place for all found function templates. For type1
, only the function template for std::pair
will survive argument deduction (and it deduces its template arguments to be std::string
and int
, respectively). However, for type2
, there is no set of template arguments that will fit because type2
is not an instantiation of a std::pair
template.
Fourth, overload resolution comes into play. For type1
, both your own function operator==
and the std::operator==
function template are of equal rank (Exact Match). Therefore the tie-break will select your non-template function. For type2
, there is only one viable function so overload resolution does not come into play and your function will be selected.
Conclusion 1: type1
and type2
will give the same answer (your version is selected), albeit for different reasons.
Expression 2
cout << (find(vec.begin(), vec.end(), v2) != vec.end()) << endl;
Here we need to first resolve the call to find
. Because of your using namespace std;
, unqualified name lookup already finds (no pun intended) std::find
, but even without the using directive, ADL on the std::vector
iterator would have found it. It will deduce the third template argument for std::find
to either type1
or type2
.
Within std::find
, a call to operator==
is found. Again, ordinary lookup will be performed first. However, this takes place from within namespace std
. It will find several operator==
function templates (for std::vector
, std::string
and std::pair
). As soon as candidates in one scope are found during unqualified name lookup, this phase of name lookup stops.
However, ADL is still being performed. Note though that the global namespace is not an associated namespace to type1
because it is only a typedef to a class in namespace std
. So for type1
, ADL does not find anything new. In contrast, type2
does have the global namespace as its associated namespace and so ADL will find your operator==
function template in that case.
For type1
, template-argument-deduction finds std::string
and int
as template arguments for the operator==
function template for std::pair
. For type2
, there is again no set of template arguments that will fit because type2
is not an instantiation of a std::pair
template.
That leaves overload resolution. For type1
, there is only one viable function (the instance of the std::operator==
template), and overloads resolution does not come into play. For type2
, there is also only one viable function (viable because it only requires a standard derived-to-base
conversion). Hence overload resolution also does not come into play.
Conclusion 2: for type1
(std
version) and type2
(your version) you get different results.
Summary
Just because these things can get very tricky with multiple overloads in different namespaces, here's a summary table with the holy trinity (name lookup, argument deduction and overload resolution). For each phase, and for each type, I have listed the surviving candidates after that phase. The bottom row shows the called function.
Expression 1
+---------------------+-----------------+-----------------+
| phase | type1 | type2 |
+---------------------+-----------------+-----------------+
| unqualified lookup | ::operator== | ::operator== |
| ADL | std::operator== | std::operator== |
+---------------------+-----------------+-----------------+
| argument deduction | ::operator== | ::operator== |
| | std::operator== | |
+---------------------+-----------------+-----------------+
| overload resolution | ::operator== | ::operator== |
+---------------------+-----------------+-----------------+
Expression 2
+---------------------+-----------------+-----------------+
| phase | type1 | type2 |
+---------------------+-----------------+-----------------+
| unqualified lookup | std::operator== | std::operator== |
| ADL | | ::operator== |
+---------------------+-----------------+-----------------+
| argument deduction | std::operator== | ::operator== |
+---------------------+-----------------+-----------------+
| overload resolution | std::operator== | ::operator== |
+---------------------+-----------------+-----------------+
Note that unqualified lookup finds a different name depending on the scope it starts in (function scope inside global scope versus namespace scope), and that ADL similarly finds a different name depending on which namespace is considered associated (namespace std
vs the global namespace).