在类的内部和外部定义的朋友功能的查找规则之间的区别

时间:2018-08-28 23:33:30

标签: c++ name-lookup

以下代码:

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,也可以找到在类外部定义的功能

但是我不太明白这意味着什么。

1 个答案:

答案 0 :(得分:2)

要注意的一件事是,类内部和外部的朋友函数的查找规则是不同的,请参见[namespace.memdef](强调我的观点)

  

如果非本地类中的朋友声明首先声明了一个类,   函数,类模板或函数模板,朋友是   最里面的封闭名称空间的成员。 朋友声明   本身不会使名称对unqualified lookup可见,或者   qualified lookup [注意:朋友的姓名将在   如果在名称空间范围内提供了匹配的声明,则为其名称空间   (在授予友谊的类定义之前或之后)。   — 尾注]如果调用了朋友功能或功能模板,则其   可以通过考虑功能的名称查找来找到名称   与函数类型关联的名称空间和类   参数([basic.lookup.argdep])。如果名字在朋友声明中   既不合格也不template-id,并且声明是一个   函数或elaborated-type-specifier,查找以确定   该实体先前是否已被宣布不得考虑任何   作用域在最内层的封闭命名空间之外。 [注意:   朋友声明的形式不能声明的新成员   最内层的封闭命名空间,因此遵循通常的查找规则。   — 尾注]

这意味着在您的第一个示例中,编译器看到了与操作数intX的比较,但是没有从Xint(或{ {1}}到int,但X也没有比较运算符)。不会尝试将两个操作数都转换为X,因为根据上面引用的子句,匹配的比较运算符不可见。

同时,您可以在第二个示例中看到具有非显式构造函数的危险,因为两个操作数都隐式转换为可能不相关的类型Y。这可能会产生非常出乎意料的行为,因为编译器认为由于语义不正确而不应编译的代码是有效的。另请参见C ++核心准则C.46: By default, declare single-argument constructors explicit