内联友元函数的范围是什么?

发布于 2024-12-17 07:27:18 字数 920 浏览 5 评论 0原文

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

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

以下代码

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

After searching aroung SO, one question taught me that the lexical scope of an inline friend function is the class it's defined in, meaning it can access e.g. the typedefs in the class without qualifying them. But then I wondered what is the actual scope of such a function? GCC at least rejects all my attempts to call it. Can a function such as in the example ever be called through means other than ADL, which is not possible here thanks to no arguments?

Standard quotations are appreciated, as I currently can't access my copy of it.

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
  }
}

results in these errors:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

七分※倦醒 2024-12-24 07:27:18

当您在类中声明具有非限定 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 并且该声明是一个函数或详细类型说明符,确定实体是否先前已声明的查找不应考虑最内部封闭命名空间之外的任何范围。

When you declare a friend function with an unqualified id in a class it names a function in the nearest enclosing namespace scope.

If that function hasn't previously been declared then the friend declaration doesn't make that function visible in that scope for normal lookup. It does make the declared function visible to argument-dependent lookup.

This is emphasised in many notes, but the definitive statement is in 7.3.1.2/3 (of ISO/IEC 14882:2011):

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

纸伞微斜 2024-12-24 07:27:18

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

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

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 中放入正确的声明,eg:

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 中找到该函数,但无法访问该函数。如 foo::baz(k),由于 ADL,您必须在 foo 中显式定义 baz(const bar&) 才能通过限定名称访问它。

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

"The C++ Programming Language 3rd Edition (Stroustrap)" : p279:

I. "Like a member declaration, a friend declaration does not introduce a name into an enclosing scope"

II. "A friend class must be previously declared in an enclosing scope or defined in the nonclass
scope immediately enclosing the class that is declaring it a friend"

III. "A friend function can be explicitly declared just like friend classes, or it can be found through its argument types (§8.2.6) as if it was declared in the nonclass
scope immediately enclosing its class."

IV. "It follows that a friend function should either be explicitly declared in an enclosing scope or take an argument of its class. If not, the friend cannot be called. For example:"

//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 :)

But in your case there is more to it that has to do with the namespace, because if you put the proper declaration in foo e.g.:

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

does not compile. However, if you declare it outside foo is works like a charm. Now consider that in fact, global, local, struct, and classes are in fact namespaces. Now this leads to the conclusion that the baz(const &) is implicitly defined in the global scope.

This compiles:

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

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

Therefore, there are two issues:

  1. The friend declaration does not introduce a name in an enclosing scope, unless IV. Thus the original program cannot find baz() because it has not been properly declared.
  2. If IV , i.e. ADL, then the function is found in foo, but cannot be accessed as foo::baz(k), due to ADL. You will have to explicitely define baz(const bar&) in foo to access it by qualified name.

Thanks, hope it helps, but certainly, I liked the challenge :) .

风月客 2024-12-24 07:27:18

有趣的!

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

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

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 Friends [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)。

注意:

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

Interesting!

It seems that the compiler does not know what scope it belongs to (and to be honest there are no clues) and thus puts in in no scope. Some standard digging coming up I suppose.

Note: If you explicitly add a declaration to a particular scope then it starts to work as expected.

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

Digging the standard I find:

11.3 Friends [class.friend]

Paragraph 6

A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope.

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

Paragraph 7

Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).

Note:

A free standing function that does not take a parameter is not much use as a friend. As it will have no object on which to take advantage of its friendship (I suppose file scope static storage duration objects).

晨曦÷微暖 2024-12-24 07:27:18

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

A friend function defined within an enclosing class, will only be found by argument dependent lookup (ADL). Calling it will succeed when one or more of the arguments is either of the enclosing class type; or of a type declared within the class. Here is an example, displaying "Hello World!", which (for variety) doesn't use an object of the enclosing class type to provide such an argument:

#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;
}
清泪尽 2024-12-24 07:27:18

在此示例中,

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.
}

In this Example,

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() is inaccessible because the name baz is not visible in scope of namespace foo. If I remember correctly (§ 3.4/2) applies here.

  2. foo::bar::baz() is inaccessible because friends aren't members of class and the scope of inline friend function is namespace or class in which their definition exists therefore, you can't access them outside of that scope.

If you put the declaration of baz() in foo the name of function baz will be visible in foo and the definition will be looked up in the nested scope of 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.
}
盗心人 2024-12-24 07:27:18

我遇到了类似的问题...
下面的代码的非直观非功能确实让我感到惊讶,尽管它可能在标准范围内:-(


#include <iostream>
#include <cassert>

    /// Alternative "fill" for `u_type`
    struct opaque {
        unsigned val;
        constexpr opaque(): val(0xffffffff){}
        constexpr opaque(int v) : val(v) {}
        constexpr opaque(const opaque& other) : val(other.val) {}
        operator int() const { return val; }
    };

    class unique_test {
    public:
        using u_type = opaque;   //!< Type which has to be "uniqued".
        //using u_type = double; //!< Other alternative. Not works same way.
        static constexpr u_type ONE=10;

        friend std::ostream& operator << (std::ostream& os, const u_type*  dummy) { return os<<"u_type"; }  //OK
        friend std::ostream& operator << (std::ostream& os, const unique_test& u) { return os<<"unique_test"; } //OK

        friend const u_type* check10(const unique_test&  dummy) { return &ONE; } //OK
        friend const u_type* check10(const unique_test*  dummy) { return &ONE; } //OK

        friend const u_type* check10(const u_type&       dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const u_type*       dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const unsigned*     dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const unsigned      dummy) { return &ONE; } //NOT VISIBLE BELOW!!!

        unique_test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check10( this );                                     assert(existed!=nullptr);
        }

        void test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check10( *this );                                          assert(existed!=nullptr);
        }
    };

    class unique_test_der : public unique_test {
        public:
        static constexpr u_type TWO=11;
        friend const u_type* check11(const unique_test*  dummy) { return &TWO; } //VISIBLE INSIDE BUT NOT OUTSIDE!
        friend const u_type* check11(const unique_test&  dummy) { return &TWO; } //VISIBLE INSIDE BUT NOT OUTSIDE!
        unique_test_der() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check11( this ); assert(existed!=nullptr);
                          existed = check11( *this ); assert(existed!=nullptr);
        }

        void test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check11( this ); assert(existed!=nullptr);
                          existed = check11( *this ); assert(existed!=nullptr);
        }
    };

    void test_uniq_test(unique_test& dumdum) {
        unsigned       dummie=1;
        unique_test::u_type dum;

        std::cout<<&dum<<' '<<dumdum<<std::endl;
        const unique_test::u_type* existed10 = check10( &dumdum ); assert(existed10!=nullptr);
                                   existed10 = check10( dumdum ); assert(existed10!=nullptr);

        const unique_test::u_type* existed11 = check11( &dumdum ); // error: ‘check10’ was not declared in this scope

        const unique_test::u_type* existed2 = check10( &dum ); // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed3 = check10( dum ) // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed4 = check10( &dummie); // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed5 = check10( dummie ); // error: ‘check10’ was not declared in this scope
    }
  

I ran into a similar problem...
The non-intuitive non-functioning of the code below really surprised me, although it's probably within the standard :-(


#include <iostream>
#include <cassert>

    /// Alternative "fill" for `u_type`
    struct opaque {
        unsigned val;
        constexpr opaque(): val(0xffffffff){}
        constexpr opaque(int v) : val(v) {}
        constexpr opaque(const opaque& other) : val(other.val) {}
        operator int() const { return val; }
    };

    class unique_test {
    public:
        using u_type = opaque;   //!< Type which has to be "uniqued".
        //using u_type = double; //!< Other alternative. Not works same way.
        static constexpr u_type ONE=10;

        friend std::ostream& operator << (std::ostream& os, const u_type*  dummy) { return os<<"u_type"; }  //OK
        friend std::ostream& operator << (std::ostream& os, const unique_test& u) { return os<<"unique_test"; } //OK

        friend const u_type* check10(const unique_test&  dummy) { return &ONE; } //OK
        friend const u_type* check10(const unique_test*  dummy) { return &ONE; } //OK

        friend const u_type* check10(const u_type&       dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const u_type*       dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const unsigned*     dummy) { return &ONE; } //NOT VISIBLE BELOW!!!
        friend const u_type* check10(const unsigned      dummy) { return &ONE; } //NOT VISIBLE BELOW!!!

        unique_test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check10( this );                                     assert(existed!=nullptr);
        }

        void test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check10( *this );                                          assert(existed!=nullptr);
        }
    };

    class unique_test_der : public unique_test {
        public:
        static constexpr u_type TWO=11;
        friend const u_type* check11(const unique_test*  dummy) { return &TWO; } //VISIBLE INSIDE BUT NOT OUTSIDE!
        friend const u_type* check11(const unique_test&  dummy) { return &TWO; } //VISIBLE INSIDE BUT NOT OUTSIDE!
        unique_test_der() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check11( this ); assert(existed!=nullptr);
                          existed = check11( *this ); assert(existed!=nullptr);
        }

        void test() {
            u_type dum;
            std::cout<<&dum<<' '<<*this<<std::endl;
            const u_type* existed = check11( this ); assert(existed!=nullptr);
                          existed = check11( *this ); assert(existed!=nullptr);
        }
    };

    void test_uniq_test(unique_test& dumdum) {
        unsigned       dummie=1;
        unique_test::u_type dum;

        std::cout<<&dum<<' '<<dumdum<<std::endl;
        const unique_test::u_type* existed10 = check10( &dumdum ); assert(existed10!=nullptr);
                                   existed10 = check10( dumdum ); assert(existed10!=nullptr);

        const unique_test::u_type* existed11 = check11( &dumdum ); // error: ‘check10’ was not declared in this scope

        const unique_test::u_type* existed2 = check10( &dum ); // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed3 = check10( dum ) // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed4 = check10( &dummie); // error: ‘check10’ was not declared in this scope
        const unique_test::u_type* existed5 = check10( dummie ); // error: ‘check10’ was not declared in this scope
    }
  
生寂 2024-12-24 07:27:18

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

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

I think you are confusing friend and private. By declaring function a friend you are granting it access to your private members, and not grating other functions access to it. Anyway, any member function of a struct is accessible by any object because struct members are public by default.

However, in your case baz isn't accessible because by doing friend void baz(){} you didn't really declare the function baz, you just said that it is a friend function. You can just remove the friend keyword, and it will solve all the issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文