以下代码:
struct X {
X() {}
};
struct Y {
Y() {}
Y(X) {}
Y(int) {}
friend bool operator==(const Y&, const Y&) { return false; }
};
bool f()
{
return 1 == X();
}
无法编译,并出现以下错误:
error: no match for 'operator==' (operand types are 'int' and 'X') return 1 == X();
虽然我将operator==
的定义移到了类之外,但效果很好:
struct X {
X() {}
};
struct Y {
Y() {}
Y(X) {}
Y(int) {}
friend bool operator==(const Y&, const Y&);
};
inline bool operator==(const Y&, const Y&) { return false; }
bool f()
{
return 1 == X();
}
有人可以解释为什么吗? (理想情况下,引用了一些标准和易于理解的解释/动机。)在这里的答案中:https://stackoverflow.com/a/20114792/1350936 @rightfold提到了
即使没有ADL,也可以找到在类外部定义的功能
但是我不太明白这意味着什么。
答案 0 :(得分:2)
要注意的一件事是,类内部和外部的朋友函数的查找规则是不同的,请参见[namespace.memdef](强调我的观点)
如果非本地类中的朋友声明首先声明了一个类, 函数,类模板或函数模板,朋友是 最里面的封闭名称空间的成员。 朋友声明 本身不会使名称对unqualified lookup可见,或者 qualified lookup。 [注意:朋友的姓名将在 如果在名称空间范围内提供了匹配的声明,则为其名称空间 (在授予友谊的类定义之前或之后)。 — 尾注]如果调用了朋友功能或功能模板,则其 可以通过考虑功能的名称查找来找到名称 与函数类型关联的名称空间和类 参数([basic.lookup.argdep])。如果名字在朋友声明中 既不合格也不template-id,并且声明是一个 函数或elaborated-type-specifier,查找以确定 该实体先前是否已被宣布不得考虑任何 作用域在最内层的封闭命名空间之外。 [注意: 朋友声明的形式不能声明的新成员 最内层的封闭命名空间,因此遵循通常的查找规则。 — 尾注]
这意味着在您的第一个示例中,编译器看到了与操作数int
和X
的比较,但是没有从X
到int
(或{ {1}}到int
,但X
也没有比较运算符)。不会尝试将两个操作数都转换为X
,因为根据上面引用的子句,匹配的比较运算符不可见。
同时,您可以在第二个示例中看到具有非显式构造函数的危险,因为两个操作数都隐式转换为可能不相关的类型Y
。这可能会产生非常出乎意料的行为,因为编译器认为由于语义不正确而不应编译的代码是有效的。另请参见C ++核心准则C.46: By default, declare single-argument constructors explicit。