我尝试使用重载的运算符==()在向量中找到一个元素。但是,如果在以下代码中使用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;
}
答案 0 :(得分:8)
std::pair
在名称空间operator==
中有默认std
,这是任意对的模板,比较first
和second
字段。在四种情况之一中挑选此运算符,即find
和TYPE == type1
。但细节有点复杂:
TYPE == type1
实际发生的事情是(如果我错了,请纠正我)
v1 == v2
ADL( Argument Dependent Name Lookup )用于查找operator==
中的std
,这意味着此运算符被添加到正常的重载集。但是,当前翻译单元中的非模板版本仍然优先于operator==
的模板std
。std::find
调用已在std
内实例化,因此operator==
的查找将直接在std
中启动。它找到一个匹配(不使用ADL!),因此不会搜索包含OP自己的运算符的封闭范围。适用于TYPE == type2
v1 == v2
很简单 - 直接在封闭名称空间中找到operator==
。std::find
也在std
中实例化,但主范围中的自定义运算符使用ADL添加到重载决策集中,然后发现比{{1}中的自定义运算符更具体}。答案 1 :(得分:5)
@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
,但即使没有using指令,std::vector
迭代器上的ADL也会找到它。它会将std::find
的第三个模板参数推断为type1
或type2
。
在std::find
内,找到了对operator==
的调用。同样,将首先执行普通查找。但是,这发生在namespace std
内。它会找到几个operator==
函数模板(适用于std::vector
,std::string
和std::pair
)。一旦在非限定名称查找期间找到一个范围中的候选者,该名称查找阶段就会停止。
但是,ADL仍在执行中。请注意,全局命名空间不是type1
的关联命名空间,因为它只是namespace std
中类的typedef 。因此对于type1
,ADL没有找到任何新内容。相反,type2
确实将全局命名空间作为其关联的命名空间,因此在这种情况下,ADL将找到您的operator==
函数模板。
对于type1
,template-argument-deduction查找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
vs全局命名空间)。
答案 2 :(得分:1)
std::pair
有自己的operator==
,优先于您自己的。{/ p>
答案 3 :(得分:1)
我认为你最好不要使用find_if代替。它需要一个谓词,因此您可以将比较器定义为普通函数/函数并传递它。