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


我最近发现朋友声明范围如下 非常特殊的规则  - 如果你有 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
}

如果你看一下标准(参见 相关答案),他们花了很大的力气来实现这种古怪的行为,在复杂规则的合格/非合格查找中添加了一个特定的异常。最终的结果让我非常困惑1,还有另一个案例要添加到实现中。作为

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

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


  1. 例如,在这种特殊情况下

    struct M {
       friend class N;
    };
    N *foo;
    typedef int N;
    

    你得到 可笑的精神分裂症错误信息

    <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,但是当你试图提供相互矛盾的声明时,立即停止玩愚蠢。


12036
2017-08-31 20:51


起源

我不太确定你在这里寻找什么。就像......为什么只有ADL才能看到? - Barry
@Barry:我问他们为什么要为这个混乱而烦恼呢?只是让它们对所有查找都可见,没有创建那些“幻影”名称的奇怪规则。标准中少了几个段落,浪费了几千个神经元,记住了特殊情况,简化了编译器实现。我的(可能是错误的)隐含的假设是,如果他们按照这个长度创建这些奇异的半可见实体,那么他们的目标是一些特定的用例,但我想不出任何。 - Matteo Italia
@MatteoItalia我刚发现了 1996年(!)论文 有提议的措辞不允许这样做。现在我只需找到理由...... - Rakete1111
还有 open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0777.pdf - Jarod42
@NirFriedman确实是相关的;阅读答案中链接的文件,了解血淋淋的细节。 - Matteo Italia


答案:


那么,为了回答这个问题,你必须看看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:模板中名称注入的替代方法 更多。


12
2017-08-31 21:18



为什么要依赖于实例化 magic?为什么该类定义不会将函数模板注入封闭的命名空间? - Brian
@Brian为什么它会成为一个功能模板? - Rakete1111
Woa这确实是一个引人注目的解释;老实说,“干净”的东西恕我直言会完全取消名称注射(即我问题中的第一个替代方案),但我怀疑它早于模板,当他们发现这个问题时,他们无法破坏兼容性? - Matteo Italia
......啊不,阅读论文注意的用例很明确,实际上确实需要模板。 TIL! - Matteo Italia
@MatteoItalia嗯,问题是它会禁用任何带有默认值的模板参数的类型教育。 template <class T, class Alloc = std::allocator<std::decay_t<T>> auto make_thing(T&& t, Alloc a = Alloc()); 之类的东西远不如以前那么有用。至少如果我理解得对。 - Deduplicator