【问题标题】:Difference between lookup rules for friend function defined inside vs outside of the class类内部与类外部定义的友元函数查找规则之间的区别
【发布时间】:2018-08-28 23:33:30
【问题描述】:

以下代码:

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 也可以找到在类之外定义的函数

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

【问题讨论】:

  • 是你不明白ADL是什么还是你不明白在那个类之外声明的函数是如何查找的?
  • @0x499602D2 我看不出 ADL 在这里是如何适用的,因为 ADL 是关于在不同的命名空间中寻找函数,但在这种情况下我根本没有任何命名空间。
  • @0x499602D2 我真正要问的是: 1. 这两种情况下的查找规则有什么区别? 2. 为什么有任何区别?
  • 我不明白为什么第一种情况应该有效。您是否希望编译器将比较中的操作数转换为任何可能的不相关类型,直到有一个具有匹配的比较运算符?
  • @HenriMenke 第一种情况和第二种情况有什么区别?是的,我希望编译器尝试隐式转换来找到匹配的函数。这正是它在第二种情况下所做的,对吧?

标签: c++ name-lookup


【解决方案1】:

需要注意的是,类内外友元函数的查找规则是不同的,见[namespace.memdef](强调我的)

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

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

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

【讨论】:

  • 您关于友元函数定义的词法范围的标准引用实际上是说在该定义中使用的名称是在类范围内查找的,因此例如类成员名称是可见的。友元函数名的查找规则在 [namespace.memdef]/3 中。
  • @aschepler,但我在 [namespace.memdef]/3 中没有看到任何关于类内部定义与类外部定义的友元函数之间的区别。或者是:“如果朋友声明中的名称既不是限定词也不是模板ID,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑任何范围在最里面的封闭命名空间之外。”结合亨利写的内容?
  • @anxieux "friend 声明本身不会使名称对非限定查找或限定查找可见。[注意:如果匹配的声明是,朋友的名称将在其命名空间中可见在命名空间范围内提供(在授予友谊的类定义之前或之后提供。)]”所以它不是直接定义在哪里,只是在类内部定义允许在类外部根本没有声明的情况,这是真正与众不同的原因。
  • @aschepler 好的,所以当我在类中定义friend 函数时,命名空间级别没有任何声明,因此查找失败,对吗?如果是这样,如果其中一个参数的类型与 Y 完全相同,为什么类中的朋友函数完全可以工作?
  • @anxieux 是的,它指的是特殊的 Argument-Dependent Lookup 规则,它可以看到在由函数参数确定的“关联类”中声明的友元函数。 [basic.lookup.argdep]/4.2.
猜你喜欢
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-22
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
相关资源
最近更新 更多