find()使用重载运算符==

时间:2013-10-04 17:57:27

标签: c++ operator-overloading overload-resolution argument-dependent-lookup name-lookup

我尝试使用重载的运算符==()在向量中找到一个元素。但是,如果在以下代码中使用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 个答案:

答案 0 :(得分:8)

std::pair在名称空间operator==中有默认std,这是任意对的模板,比较firstsecond字段。在四种情况之一中挑选此运算符,即findTYPE == type1。但细节有点复杂:

TYPE == type1实际发生的事情是(如果我错了,请纠正我)

  • for 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的答案在几个细节上是不完整的。那么让我们为表达式和两种类型播放编译器,不管吗?

表达式1

cout << (v1 == v2) << endl;

首先,对于type1type2非限定名称查找main()功能范围向外开始,并找到您自己的operator==在全球范围内运作。

其次,依赖于参数的名称查找(ADL)从operator==中找到std::pair的函数模板namespace std。实际上,ADL发现了更多std::operator==个功能模板(来自std::vectorstd::string的模板,因为您也包含了这些模板)。

注意:ADL还会找到type2的匹配项,因为它的基类type1会将namespace std添加到其关联的命名空间集合中。


3.4.2依赖于参数的名称查找[basic.lookup.argdep]

  

- 如果T是类类型(包括联合),则其关联的类是:   班级本身;它所属的成员,如果有的话;和它的   直接和间接基类。其关联的命名空间是   其关联类是成员的名称空间。


第三,对所有找到的函数模板进行模板参数推导。对于type1,只有std::pair的函数模板才能在参数推导中存活(并且它的模板参数分别推导为std::stringint)。但是,对于type2,没有适合的模板参数集,因为type2不是std::pair模板的实例化。

第四,重载决议发挥作用。对于type1,您自己的函数operator==std::operator==函数模板具有相同的等级(完全匹配)。因此,平局决定将选择您的非模板功能。对于type2,只有一个可行的函数,因此重载分辨率不起作用,您的函数将被选中。

结论1 type1type2会给出相同的答案(您的版本已被选中),尽管原因各有不同。

表达式2

cout << (find(vec.begin(), vec.end(), v2) != vec.end()) << endl;

我们需要先解决对find的调用。由于你的using namespace std;,非限定名称查找已找到(没有双关语)std::find,但即使没有using指令,std::vector迭代器上的ADL也会找到它。它会将std::find的第三个模板参数推断为type1type2

std::find内,找到了对operator==的调用。同样,将首先执行普通查找。但是,这发生在namespace std内。它会找到几个operator==函数模板(适用于std::vectorstd::stringstd::pair)。一旦在非限定名称查找期间找到一个范围中的候选者,该名称查找阶段就会停止。

但是,ADL仍在执行中。请注意,全局命名空间不是type1 的关联命名空间,因为它只是namespace std中类的typedef 。因此对于type1,ADL没有找到任何新内容。相反,type2确实将全局命名空间作为其关联的命名空间,因此在这种情况下,ADL将找到您的operator==函数模板。

对于type1,template-argument-deduction查找std::stringint作为operator== std::pair函数模板的模板参数。对于type2,再次没有适合的模板参数集,因为type2不是std::pair模板的实例化。

留下重载决议。对于type1,只有一个可行的函数(std::operator==模板的实例),并且重载解析不起作用。对于type2,也只有一个可行的功能(可行,因为它只需要标准的derived-to-base转换)。因此,重载分辨率也没有发挥作用。

结论2 :对于type1std版本)和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代替。它需要一个谓词,因此您可以将比较器定义为普通函数/函数并传递它。