内联朋友功能的范围是什么?

时间:2011-11-21 05:26:12

标签: c++ scope inline friend

在搜索到SO之后,一个问题告诉我内联友元函数的词汇范围是它定义的类,这意味着它可以访问例如班级中的typedef没有符合条件。但后来我想知道这个函数的实际范围是什么? 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

6 个答案:

答案 0 :(得分:35)

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

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

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

  

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

答案 1 :(得分:4)

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

予。 “就像成员声明一样,朋友声明不会在封闭范围中引入名称”

II。 “友元类必须先在封闭范围内声明,或在非类中定义 范围立即附上宣布为朋友的类“

III。 “友元函数可以像朋友类一样显式声明,或者可以通过其参数类型(第8.2.6节)找到它,就好像它是在非类中声明的那样 范围立即封闭其类。“

IV。 “因此,友元函数应该在封闭范围内显式声明,或者取其类的参数。如果不是,则不能调用该朋友。例如:”

//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. 除非IV,否则朋友声明不会在封闭范围内引入名称。因此原始程序找不到baz(),因为它没有被正确声明。
  2. 如果是IV,即ADL,那么函数在foo中找到,但由于ADL,不能作为foo :: baz(k)访问。您必须在foo中明确定义baz(const bar&)以通过限定名称访问它。
  3. 谢谢,希望它有所帮助,当然,我喜欢挑战:)。

答案 2 :(得分: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)。

注意:

不带参数的独立功能作为朋友没什么用处。因为它没有利用其友谊的对象(我认为文件范围是静态存储持续时间对象)。

答案 3 :(得分:0)

在本例中,

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()无法访问,因为朋友不是类的成员,并且内联友元函数的范围是名称空间或类,因此,您不能在该范围之外访问它们。

  3. 如果您将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.
    }
    

答案 4 :(得分:0)

在封闭类中定义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;
}

答案 5 :(得分:-2)

我认为你让friendprivate感到困惑。通过声明函数friend,您授予它访问您的私有成员的权限,而不是让其他函数访问它。无论如何,任何对象都可以访问struct的任何成员函数,因为struct成员默认是公共的。

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