朋友声明的复杂范围规则有什么意义?

问题描述 投票:11回答:1

我最近发现朋友声明范围遵循extremely peculiar rules - 如果你有一个函数或一个尚未声明的类的friend声明(定义),它会在直接封闭的命名空间中自动声明(定义),但它对于非隐藏 - 合格和​​合格的查询;但是,友元函数声明通过依赖于参数的查找保持可见。

struct M {
    friend void foo();
    friend void bar(M);
};

void baz() {
    foo();    // error, unqualified lookup cannot find it
    ::foo();  // error, qualified lookup cannot find it
    bar(M()); // ok, thanks to ADL magic
}

如果你看一下标准(参见linked answer),他们就会花费很大的精力来启用这种古怪的行为,在复杂规则的限定/非限定查找中添加一个特定的异常。最终的结果让我感到非常困惑1,还有另一个案例要添加到实现中。作为

  • 要求friend声明引用现有名称,期限;要么
  • 允许他们像现在一样声明东西,但不改变普通的名字查找(因此,这样的名称变得可见,好像在封闭的命名空间中声明为“正常”)

似乎更容易实现,指定,最重要的是,理解,我想知道:为什么他们为这个烂摊子烦恼?他们试图覆盖哪些用例?在任何这些更简单的规则下(特别是第二个与现有行为最相似的规则)会破坏什么?


  1. 例如,在这种特殊情况下 struct M { friend class N; }; N *foo; typedef int N; 你得到comically schizophrenic error messages <source>:4:1: error: 'N' does not name a type N *foo; ^ <source>:5:13: error: conflicting declaration 'typedef int N' typedef int N; ^ <source>:2:17: note: previous declaration as 'class N' friend class N; ^ 编译器首先声称没有N这样的东西,但是当你试图提供冲突的声明时立即停止玩愚蠢。
c++ language-lawyer friend friend-function language-history
1个回答
13
投票

那么,为了回答这个问题,你必须看看C ++的另一个主要特性:模板。

考虑一个这样的模板:

template <class T>
struct magic {
    friend bool do_magic(T*) { return true; }
};

在这样的代码中使用:

bool do_magic(void*) { return false; }

int main() {
    return do_magic((int*)0);
}

退出代码是0还是1

那么,这取决于magic是否曾经用int实例化任何可观察的。 至少它会,如果friend函数只声明内联将通过普通的查找规则找到。 而且你不能通过注入所有可能的东西来打破这个难题,因为模板可以是专用的。

这种情况曾经是一段时间,但被视为“太神奇”,“太不明确”。

名称注入还存在其他问题,因为它没有像希望的那样明确定义。有关更多信息,请参阅N0777: An Alternative to Name Injection from Templates

© www.soinside.com 2019 - 2024. All rights reserved.