朋友声明的复杂范围规则有什么意义? [英] What is the point of the complicated scoping rules for friend declarations?

查看:108
本文介绍了朋友声明的复杂范围规则有什么意义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近发现,朋友声明的作用域遵循极其特殊的规则-如果您有friend声明(定义)对于尚未声明的函数或类,将在紧邻的名称空间中自动声明(定义),但是对于不合格和合格的查找是不可见的;但是,通过依赖于参数的查找,朋友的 function 声明仍然可见.

I recently discovered that friend declarations scoping follows extremely peculiar rules - if you have a friend declaration (definition) for a function or a class that is not already declared, it is automatically declared (defined) in the immediately enclosing namespace, but it is invisible to non-qualified and qualified lookup; however, friend function declarations remain visible through argument-dependent lookup.

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 极度令人困惑,同时又给实现添加了另一种极端的情况.还是

If you look at the standard (see linked answer), they went to significant lengths to enable this eccentric behavior, adding a specific exception in qualified/non qualified lookup with complex rules. The end result looks to me extremely confusing1, with yet-another-corner case to add to implementations. As either

  • 要求friend声明引用现有名称,句点;或
  • 允许他们声明现在的内容,但不更改普通名称查找(因此,这些名称就像在封闭的名称空间中正常声明"一样可见)
  • requiring friend declarations to refer to existing names, period; or
  • allowing them to declare stuff as it is now, but without altering the ordinary names lookup (so, such names become visible as if declared "normally" in the enclosing namespace)

似乎更易于实现,指定以及最重要的是要理解,我想知道:为什么他们要为这个混乱而烦恼?他们试图涵盖哪些用例?在那些更简单的规则(尤其是第二条规则,与现有行为最相似)中,有什么破绽?

seem simpler to implement, to specify and, most importantly, to understand, I wonder: why did they bother with this mess? What use cases were they trying to cover? What breaks under any of those simpler rules (in particular the second one, which is the most similar to the existing behavior)?

  1. 例如,在这种情况下

  1. For example, in this particular case

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这样的东西,但是当您尝试提供有冲突的声明时,立即停止播放哑音.

where the compiler first claims that there's no such a thing as N, but immediately stops playing dumb when you try to provide a conflicting declaration.

推荐答案

为回答这个问题,您必须查看C ++的另一个主要功能:模板.

Well, for answering that, you have to look at another major feature of C++: Templates.

考虑这样的模板:

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?

好吧,这取决于是否可以在任何可观察到的地方用int实例化magic.
至少,如果普通查找规则可以找到仅声明为内联的friend -functions.
而且,由于可以专门化模板,因此不能仅通过注入所有可能的方法来解决这个难题.

Well, it depends on whether magic was ever instantiated with int anywhere observable.
At least it would, if friend-functions only declared inline would be found by ordinary lookup-rules.
And you can't break that conundrum by just injecting everything possible, as templates can be specialized.

曾经是这种情况,但是被禁止为太不可思议"和太不明确".

That was the case for a time, but was outlawed as "too magic", and "too ill-defined".

名称注入还存在其他问题,因为它的定义不尽如人意.参见 N0777:从模板注入名称的替代方法了解更多信息.

There were additional problems with name injection, as it wasn't nearly as well-defined as hoped for. See N0777: An Alternative to Name Injection from Templates for more.

这篇关于朋友声明的复杂范围规则有什么意义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆