代码之家  ›  专栏  ›  技术社区  ›  Matteo Italia

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

  •  11
  • Matteo Italia  · 技术社区  · 6 年前

    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 ,还有一个角落案例需要添加到实现中。作为其中之一

    • 需要 朋友
    • 允许它们按现在的样子声明内容,但不改变普通名称查找(因此,这样的名称变得可见,就像在封闭的命名空间中声明“正常”一样)

    实现起来似乎更简单,更具体,更重要的是,理解起来更简单,我想知道:他们为什么要为这一团糟而烦恼?他们试图涵盖哪些用例?在这些更简单的规则下(特别是第二个规则,它与现有的行为最为相似)有什么突破?


    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

    1 回复  |  直到 6 年前
        1
  •  13
  •   Deduplicator    6 年前

    为了回答这个问题,你必须看看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 更多。