8

我尝试使用重载运算符==() 在向量中查找元素。但是,如果type1在以下代码中使用,则输出为 1 和 0(未找到)。Usingtype2给出 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

4 回答 4

8

std::pairoperator==在 namespace中具有默认值std,它是任意对的模板,比较 thefirstsecond字段。该运算符在四种情况之一中被选中,即findwith TYPE == type1。不过细节有点复杂:

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

  • for v1 == v2ADL ( Argument Dependent Name Lookup ) 用于查找operator==in std,这意味着该运算符被添加到正常的重载集中。但是,当前翻译单元中的非模板版本仍然比operator==来自std.
  • std::find调用在 中实例化std,因此查找operator==直接在 中开始std。它找到一个匹配项(不使用 ADL!),因此不会搜索包含 OP 自己的运算符的封闭范围。

而对于TYPE == type2

  • v1 == v2很容易——它直接operator==在封闭的命名空间中找到 。
  • std::find也被实例std化,但是来自主范围的自定义运算符使用 ADL 添加到重载解决方案集中,然后发现比std.
于 2013-10-04T18:00:57.023 回答
5

@AlexanderGessler 的回答在几个细节上是不完整的。那么让我们为两种表达式和两种类型都运行编译器,好吗?

表达式 1

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

首先,对于type1and type2非限定名称查找main()函数范围向外开始,并operator==在全局范围内找到您自己的函数。

其次,参数相关名称查找(ADL) 查找from的函数模板operator==。实际上,ADL 找到了更多的函数模板(来自和的那些,因为您也包含了这些标题)。std::pairnamespace stdstd::operator==std::vectorstd::string

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


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

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


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

第四,重载决议开始发挥作用。对于type1,您自己的函数operator==std::operator==函数模板的等级相同(完全匹配)。因此,抢七将选择您的非模板函数。对于type2,只有一个可行的函数,因此重载决议不会发挥作用,您的函数将被选中。

结论 1type1并且type2会给出相同的答案(您的版本被选中),尽管原因不同。

表达式 2

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

在这里,我们首先需要解决对find. 由于您的using namespace std;,未限定名称查找已经找到(没有双关语)std::find,但即使没有 using 指令,std::vector迭代器上的 ADL 也会找到它。std::find它将推导出totype1或的第三个模板参数type2

在 内std::find,找到一个调用operator==。同样,将首先执行普通查找。然而,这发生在内部namespace std。它将找到几个operator==函数模板(for和std::vector)。一旦在不合格的名称查找过程中发现了一个范围内的候选者,名称查找的这个阶段就会停止。std::stringstd::pair

但是,ADL 仍在执行中。请注意,尽管全局命名空间不是关联的命名空间,因为type1 它只是. namespace std所以对于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与全局命名空间)找到不同的名称。

于 2013-10-04T22:45:13.467 回答
1

std::pair有它自己operator==的优先于你自己的。

于 2013-10-04T18:01:32.197 回答
1

我认为你最好使用find_if而不是 find。它需要一个谓词,因此您可以将比较器定义为普通函数/函子并传递它。

于 2013-10-04T18:09:20.193 回答