【问题标题】:What's the scope of inline friend functions?内联友元函数的范围是什么?
【发布时间】:2012-01-02 16:55:36
【问题描述】:

在搜索了 SO 之后,一个问题告诉我,内联友元函数的 词法 范围是它定义的类,这意味着它可以访问例如班级中的typedefs 没有限定他们。但后来我想知道这种函数的实际作用域是什么? GCC 至少拒绝了我调用它的所有尝试。可以通过 ADL 以外的方式调用示例中的函数吗?由于没有参数,这在此处是不可能的?

感谢标准报价,因为我目前无法访问我的副本。

The following code

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}

导致这些错误:

prog.cpp: In function ‘int main()’:
prog.cpp:9: error: ‘baz’ is not a member of ‘foo’
prog.cpp:10: error: ‘baz’ is not a member of ‘foo::bar’
prog.cpp: In member function ‘void foo::bar::call_friend()’:
prog.cpp:15: error: ‘baz’ was not declared in this scope

【问题讨论】:

  • 最好在你的问题中加入一些代码,而不是链接中的所有内容。
  • @iammilind:哎呀,我的意思是,不知何故忘记了...编辑。
  • 11.3.7 说“在类中定义的友元函数在定义它的类的(词法)范围内。”
  • 在 C++ 中的朋友可以访问您的私人会员。
  • @Cat:这就是我对第一部分的引用,关于访问 typedef。但这并不适用于真正的范围,否则我应该能够使用 try #2 访问它...

标签: c++ scope inline friend


【解决方案1】:

我认为您混淆了friendprivate。通过将函数声明为friend,您将授予它访问您的私有成员的权限,而不是授予其他函数访问它的权限。无论如何,struct 的任何成员函数都可以被任何对象访问,因为struct 成员默认情况下是公共的。

但是,在您的情况下,baz 无法访问,因为通过执行 friend void baz(){} 您并没有真正声明函数 baz,您只是说它是 friend 函数。您只需删除 friend 关键字即可解决所有问题。

【讨论】:

  • 在您的编辑中:不,我肯定不会混淆这两者。而friend void bar(){} 是一个完美的函数定义。
  • 如果他删除了朋友声明,那么它就变成了一个成员而不是一个独立的函数(这可能不是这里想要的)。我认为它是一个结构的事实只是为了简化示例。
【解决方案2】:

有趣!

似乎编译器不知道它属于哪个作用域(老实说没有线索),因此没有放入任何作用域。我想一些标准的挖掘即将到来。

注意:如果您将声明显式添加到特定范围,则它开始按预期工作。

namespace foo
{
  void baz();   // declare it here and now it works in foo namespace etc.
  struct bar
  {
    friend void baz(){}
    void call_friend();
  };
}

挖掘我找到的标准:

11.3 朋友 [class.friend]

第 6 段

当且仅当类是非本地类 (9.8)、函数名不合格且函数具有命名空间范围时,才能在类的友元声明中定义函数.

[ Example:
class M { friend void f() { }       // definition of global f, a friend of M,
                                    // not the definition of a member function
};
— end example ]

第 7 段

这样的函数是隐式内联的。 类中定义的友元函数位于定义它的类的(词法)范围内。在类外定义的友元函数不是 (3.4.1)。

注意:

不带参数的独立函数作为朋友用处不大。因为它没有对象可以利用它的友谊(我想文件范围静态存储持续时间对象)。

【讨论】:

  • 我想知道“函数具有命名空间范围”位。这是否意味着生成的函数现在具有命名空间范围,或者被内联定义为友元的函数应该在定义之前具有命名空间范围?
  • @MatthieuM.:我读到的方式是该函数属于命名空间而不是类(尽管似乎未指定哪个命名空间)。
  • @Matthieu:我也在读后者,因为它似乎是“如果且仅当”子句的一部分。
  • @MartinYork 我在本地类中定义了friend 函数,我仍然可以在main() 中访问它。这与您在 第 6 段下的块引用中所写的内容相冲突。
  • @Lucas 符合预期。该函数在与类相同的命名空间中可见。如果这是在全局命名空间中,那么它应该是可见的,而无需在 main() 中进行限定。这符合预期。
【解决方案3】:

在这个例子中,

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}
  1. foo::baz() 无法访问,因为名称 baz 在命名空间 foo 的范围内不可见。如果我没记错的话(§ 3.4/2)适用于此。

  2. foo::bar::baz() 不可访问,因为好友不是类的成员,并且内联好友函数的范围是名称空间或存在其定义的类,因此,您无法在该范围之外访问它们。

如果将baz() 的声明放在foo 中,则函数名称baz 将在foo 中可见,并且将在bar 的嵌套范围内查找定义。

namespace foo{
  void baz();  // declaration at namespace scope
  struct bar{
    friend void baz(){}
  };

  void call_friend() {
     baz();  // ok
  }
}

int main()
{
    foo::baz(); // ok, bar will be looked up in the nested scope of foo::bar.
}

【讨论】:

  • 好吧,但是 baz 肯定是在 bar 中定义的,所以根据您的说法,这应该是它的范围,但事实并非如此。 :(
【解决方案4】:

当您在类中声明具有非限定 id 的 friend 函数时,它会在最近的封闭命名空间范围内命名一个函数。

如果该函数之前没有被声明,那么friend 声明不会使该函数在该范围内可见以进行正常查找。它确实使声明的函数对依赖于参数的查找可见。

许多注释都强调了这一点,但最终声明在 7.3.1.2/3(ISO/IEC 14882:2011)中:

在命名空间中首先声明的每个名称都是该命名空间的成员。如果非本地类first 中的friend 声明声明了一个类或函数,则友元类或函数是最内层封闭命名空间的成员。 在该命名空间范围内提供匹配声明之前,非限定查找 (3.4.1) 或限定查找 (3.4.3) 无法找到朋友的名称(在类定义之前或之后)给予友谊)。如果调用友元函数,则可以通过名称查找找到其名称,该名称查找考虑来自与函数参数类型相关联的命名空间和类的函数(3.4.2)。如果 friend 声明中的名称既不是限定的也不是 template-id 并且声明是函数或 elaborated-type-specifier,则查找以确定是否先前已声明的实体不应考虑最内层封闭命名空间之外的任何范围。

【讨论】:

  • 完美,甚至是 C++03 标准中的同一段。完全隐藏了该声明...谢谢!
【解决方案5】:

“C++ 编程语言第 3 版(Stroustrap)”:p279:

我。 “与成员声明一样,友元声明不会将名称引入封闭范围”

二。 “朋友类必须事先在封闭范围内声明或在非类中定义 作用域立即封闭了声明它为朋友的类"

三。 “友元函数可以像友元类一样显式声明,也可以通过其参数类型(第 8.2.6 节)找到,就好像它是在非类中声明的一样 作用域立即包含它的类。"

四。 "由此可见,友元函数要么在封闭作用域内显式声明,要么接受其类的参数。否则,无法调用友元。例如:"

//no f() here
void g();
class X{
    friend void f();          //useless
    friend void g();          //can be found because it is declared outside of class scope
    friend void h(const X&);  //can be found because the arguments access class members
};

void f() { }                 //enemy of X :)

但在你的情况下,它与命名空间有关,因为如果你在 foo 中放置正确的声明,例如:

namespace foo{
  struct bar{
    friend void baz(const &bar){};
    void call_friend();
  }
}

不编译。但是,如果你在 foo 之外声明它就像一个魅力。现在考虑事实上,全局、局部、结构和类实际上是命名空间。现在这导致了baz(const &) 被隐式定义在全局范围内的结论。

这样编译:

namespace foo{
  struct bar{
    friend void baz(const bar&){};
    void call_friend();
  };
}

int main(){
    foo::bar k;
    baz(k);
    return 0;
}

因此,有两个问题:

  1. friend 声明不会在封闭范围内引入名称,除非 IV.因此原始程序找不到 baz() 因为它没有被正确声明。
  2. 如果 IV ,即 ADL,则该函数在 foo 中找到,但由于 ADL,无法作为 foo::baz(k) 访问。您必须在 foo 中明确定义 baz(const bar&) 才能通过限定名称访问它。

谢谢,希望对您有所帮助,但当然,我喜欢这个挑战 :)。

【讨论】:

  • it is found in the global scope - 不是真的,它只是不能通过命名空间foo 中的合格查找来调用。这并不意味着它在全球范围内。此外,@Charles 发布的标准引用似乎与 f 无用相矛盾:(either before or **after** the class definition granting friendship),这就是你在这里所做的(或者void f(){}X 之后也在书中?我相信不是)。
  • 它在 X 之后。你应该看看这本书。我引用了页面。 sn-p 是从那里复制的。
  • @Xeo:我编辑增加了书中的几行,并添加了 call_friend() 代码的定义。
  • 有趣。但这意味着 Bjarne 与该 sn-p 中的标准相矛盾,因为该标准明确允许在类定义之后定义友元函数。
  • 我不得不承认,尽管 baz(k) 不在全局范围内,因为 ::baz(k) 不会编译并出现错误:“错误:全局范围没有“baz””。但是,似乎是通过不合格的查找找到的,这确实很奇怪,因为 foo::baz(k) 仍然失败。由于 III,我希望根据定义在 foo 中。因此第 2 点不成立。我正在调查它。 :\
【解决方案6】:

封闭类中的friend 函数定义,只能通过参数相关查找 (ADL) 找到。当一个或多个参数是封闭类类型时,调用它会成功;或在类中声明的类型。这是一个示例,显示“Hello World!”,它(为了多样化)使用封闭类类型的对象来提供这样的参数:

#include <iostream>

struct foo
{
  struct local_class{};
  friend void greet(local_class o, int) { std::cout << "Hello World!\n"; }
};

int main(int argc, char *argv[])
{
  foo::local_class o;
  greet(o,1);

  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多