Friend functions and scoping

Discussion in 'C++' started by Paul Bibbings, Apr 13, 2009.

  1. In §11.5.1 of TC++PL (Special Edition, 2000), "Finding Friends,"
    Stroustrup says:

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

    A few lines earlier in the introduction to the section (11.5,
    "Friends") he'd explicitly compared friend function declarations to
    member function declarations, saying:

    "Like a member function, a friend function is explicitly declared
    in the declaration of the class of which it is a friend. It is
    therefore as much a part of that interface as is a member function."

    This is confusing me a little when I find, through working examples,
    that the contrary of his first statement 'appears' to be the case,
    despite his overall emphasis on the similarity of the two cases. Thus,
    to simplify his example to working code, consider:

    #include <iostream>

    namespace N {
    class C {
    char *_c;
    public:
    C(char *c): _c(c) { }
    friend void f(C); // friend function
    void g(); // member function
    };

    void (*p)(C) = &f; // [Stroustrup - error: no f()
    in scope] compiles fine, with definition below
    }

    // adding the definitions ...
    void N::f(C c) { // friend scope: N::
    std::cout << c._c << '\n';
    }

    void N::C::g() { // member scope: N::C::
    std::cout << "In g()..." << '\n';
    }

    int main() {
    N::C c("Hello, World!");
    f(c); // uses the type of
    parameter c to locate f() in N::
    c.g();

    return 0;
    }

    As the definitions of member g() and friend f() show, whereas g is
    scoped in this example to the class definition, that is to N::C::, f
    is scoped to N::, which I would see as the "enclosing scope" of C. So
    I'm a little confused by his "LIKE a member declaration, a friend
    declaration does NOT introduce a name into an enclosing scope."
    Rather, unlike the member declaration, the friend declaration seems to
    being doing just that, unless I am not understanding his words
    correctly.

    Furthermore, the definition of the function pointer is supposed to
    fail, there being "no f() in scope." Of course, without the definition
    of f below this it does indeed fail at linking, but with the
    definition of f in place as above, everything appears to be fine.

    Very possibly I'm merely missing the intent of his meaning, but I'm
    struggling to find a sense that better fits his words. In particular,
    where the example he gives does not explicitly include namespacing,
    thus putting f() in the global namespace, it's hard to see exactly
    what "enclosing scope" it doesn't go in to.

    Note: The above has compiled for me with gcc 3.4.4 (cygwin) and cl v.
    15.00.30729.01 (VC++ Express), just in case this is a non-standard
    compiler issue.

    Any help with understanding this would be greatly appreciated.

    Regards PB
     
    Paul Bibbings, Apr 13, 2009
    #1
    1. Advertising

  2. Paul Bibbings

    Stefan Ram Guest

    Paul Bibbings <> writes:
    > "Like a member function, a friend function is explicitly declared
    >in the declaration of the class of which it is a friend. It is
    >therefore as much a part of that interface as is a member function."


    I am not sure, whether this is always true.

    Here, »g« seems to be declared within the class specifier:

    class K { friend void g(); };
    void g() {}
    int main() { g(); }

    But when »g« is qualified, it does not seem to be declared
    anymore:

    class K { friend void ::g(); };
    void g() {}
    int main() { g(); }

    "ComeauTest.c", line 1: error: the global scope has no "g"
    class K { friend void ::g(); };
    ^

    (gcc seems to accept this without an error message.)

    One can remove the error by adding an additional declaration:

    void g();
    class K { friend void ::g(); };
    void g() {}
    int main() { g(); }
     
    Stefan Ram, Apr 13, 2009
    #2
    1. Advertising

  3. Paul Bibbings

    James Kanze Guest

    On Apr 13, 8:16 am, Paul Bibbings <>
    wrote:
    > In §11.5.1 of TC++PL (Special Edition, 2000), "Finding Friends,"
    > Stroustrup says:


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


    > A few lines earlier in the introduction to the section (11.5,
    > "Friends") he'd explicitly compared friend function
    > declarations to member function declarations, saying:


    > "Like a member function, a friend function is explicitly declared
    > in the declaration of the class of which it is a friend. It is
    > therefore as much a part of that interface as is a member function."


    > This is confusing me a little when I find, through working
    > examples, that the contrary of his first statement 'appears'
    > to be the case, despite his overall emphasis on the similarity
    > of the two cases.


    Two possibilities behind your observations:

    -- You're using an old compiler. In pre-standard C++, a friend
    declaration injected the name into the surrounding scope, so
    that it could be found. If this is the case, upgrade your
    compiler.

    -- You're confused by ADL. Although the friend name isn't
    injected into the surrounding scope, friends will typically
    have some argument which depends on the class, and will
    cause the compiler to look into the class because of
    argument dependent lookup.

    > Thus, to simplify his example to working code, consider:
    >
    > #include <iostream>


    > namespace N {
    > class C {
    > char *_c;
    > public:
    > C(char *c): _c(c) { }
    > friend void f(C); // friend function
    > void g(); // member function
    > };


    > void (*p)(C) = &f; // [Stroustrup - error: no f()
    > in scope] compiles fine, with definition below


    This should NOT compile. At this point in the code, there is no
    name f which is visible.

    > }


    > // adding the definitions ...
    > void N::f(C c) { // friend scope: N::
    > std::cout << c._c << '\n';
    > }


    > void N::C::g() { // member scope: N::C::
    > std::cout << "In g()..." << '\n';
    > }


    > int main() {
    > N::C c("Hello, World!");
    > f(c); // uses the type of
    > parameter c to locate f() in N::
    > c.g();


    > }


    > As the definitions of member g() and friend f() show, whereas
    > g is scoped in this example to the class definition, that is
    > to N::C::, f is scoped to N::, which I would see as the
    > "enclosing scope" of C. So I'm a little confused by his "LIKE
    > a member declaration, a friend declaration does NOT introduce
    > a name into an enclosing scope." Rather, unlike the member
    > declaration, the friend declaration seems to being doing just
    > that, unless I am not understanding his words correctly.


    It's difficult to explain clearly. The fully qualified name of
    the function f, after the friend declaration, is ::N::f. The
    function itself is in the surrounding namespace scope. But the
    friend declaration does not introduce the name into this scope;
    after the friend declaration, the name is still only visible in
    the class scope ::N::C.

    > Furthermore, the definition of the function pointer is
    > supposed to fail, there being "no f() in scope." Of course,
    > without the definition of f below this it does indeed fail at
    > linking, but with the definition of f in place as above,
    > everything appears to be fine.


    Not with my compiler. It looks like you're using an out of date
    compiler, or you've encountered a bug in the compiler.

    > Very possibly I'm merely missing the intent of his meaning,
    > but I'm struggling to find a sense that better fits his words.
    > In particular, where the example he gives does not explicitly
    > include namespacing, thus putting f() in the global namespace,
    > it's hard to see exactly what "enclosing scope" it doesn't go
    > in to.


    > Note: The above has compiled for me with gcc 3.4.4 (cygwin)
    > and cl v. 15.00.30729.01 (VC++ Express), just in case this is
    > a non-standard compiler issue.


    It doesn't compile with g++ 4.3.2.

    Note that compilers may have delayed suppressing the injection
    of friend names into the surrounding scope for fear of breaking
    existing code. (At the least, I would expect a number of
    versions where such code as the above triggered a warning,
    rather than an error.) The fact remains that it is, according
    to the standard, illegal.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Apr 13, 2009
    #3
  4. Paul Bibbings

    James Kanze Guest

    On Apr 13, 11:25 am, -berlin.de (Stefan Ram) wrote:
    > Paul Bibbings <> writes:
    > > "Like a member function, a friend function is explicitly declared
    > >in the declaration of the class of which it is a friend. It is
    > >therefore as much a part of that interface as is a member function."


    > I am not sure, whether this is always true.


    Which part isn't true?

    > Here, »g« seems to be declared within the class specifier:


    > class K { friend void g(); };
    > void g() {}
    > int main() { g(); }


    > But when »g« is qualified, it does not seem to be declared
    > anymore:


    > class K { friend void ::g(); };
    > void g() {}
    > int main() { g(); }


    > "ComeauTest.c", line 1: error: the global scope has no "g"
    > class K { friend void ::g(); };


    > (gcc seems to accept this without an error message.)


    But in g++, then.

    > One can remove the error by adding an additional declaration:


    > void g();
    > class K { friend void ::g(); };
    > void g() {}
    > int main() { g(); }


    This has nothing to do with friends. After a scope resolution
    operator, qualified name lookup applies, and if the name is not
    found, it is an error. The first declaration of a name cannot
    use a qualified name.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Apr 13, 2009
    #4
  5. Paul Bibbings

    James Kanze Guest

    On Apr 13, 4:53 pm, (blargg) wrote:
    > James Kanze wrote:


    [...]
    > > It's difficult to explain clearly. The fully qualified name of
    > > the function f, after the friend declaration, is ::N::f. The
    > > function itself is in the surrounding namespace scope. But the
    > > friend declaration does not introduce the name into this scope;
    > > after the friend declaration, the name is still only visible in
    > > the class scope ::N::C.


    > [..]


    > I encountered this a while back and the best mental model I
    > could come up with is that non-member functions have a boolean
    > flag that specifies whether it is accessible normally. A
    > friend declaration introduces the function into the
    > surrounding scope, but sets this flag so that is is NOT
    > accessible normally (unless the function already was declared
    > in that scope, in which case it leaves the flag alone so that
    > it is accessible normally as it already was).


    > Declaring a function in the surrounding scope, after it's
    > already been declared a friend, toggles this flag so mat it's
    > accessible normally.


    I'm not sure I understand this. The best mental model I've been
    able to come up with (for me, anyway) is to distinguish between
    scope and visibility. Most declarations make the symbol visible
    in the scope they declare the symbol in, so there distinction is
    meaning less. The friend declaration, however, declares the
    name in scope ::N, but only makes it visible in the class scope
    ::N::C. The scope the name is declared in and the scope in
    which it is visible are different. (Off hand, I can't think of
    any other cases where this is the case. And I would guess that
    the motivation for friend name injection, in pre-standard C++,
    is that since you're declaring a name in namespace scope---file
    scope back then---, it should be visible in namespace scope.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Apr 14, 2009
    #5
  6. James Kanze <> writes:

    > On Apr 13, 4:53 pm, (blargg) wrote:
    >> James Kanze wrote:

    >
    > [...]
    >> > It's difficult to explain clearly. The fully qualified name of
    >> > the function f, after the friend declaration, is ::N::f. The
    >> > function itself is in the surrounding namespace scope. But the
    >> > friend declaration does not introduce the name into this scope;
    >> > after the friend declaration, the name is still only visible in
    >> > the class scope ::N::C.

    >
    >> [..]

    >
    >> I encountered this a while back and the best mental model I
    >> could come up with is that non-member functions have a boolean
    >> flag that specifies whether it is accessible normally. A
    >> friend declaration introduces the function into the
    >> surrounding scope, but sets this flag so that is is NOT
    >> accessible normally (unless the function already was declared
    >> in that scope, in which case it leaves the flag alone so that
    >> it is accessible normally as it already was).

    >
    >> Declaring a function in the surrounding scope, after it's
    >> already been declared a friend, toggles this flag so mat it's
    >> accessible normally.

    >
    > I'm not sure I understand this. The best mental model I've been
    > able to come up with (for me, anyway) is to distinguish between
    > scope and visibility. Most declarations make the symbol visible
    > in the scope they declare the symbol in, so there distinction is
    > meaning less. The friend declaration, however, declares the
    > name in scope ::N, but only makes it visible in the class scope
    > ::N::C. The scope the name is declared in and the scope in
    > which it is visible are different. (Off hand, I can't think of
    > any other cases where this is the case. And I would guess that
    > the motivation for friend name injection, in pre-standard C++,
    > is that since you're declaring a name in namespace scope---file
    > scope back then---, it should be visible in namespace scope.)


    using namespace N; using N::S; or just N::S are three other ways to
    make a name visible in a different scope than the one it was declared
    in.

    --
    __Pascal Bourguignon__
     
    Pascal J. Bourguignon, Apr 14, 2009
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Hans De Winter
    Replies:
    4
    Views:
    352
    Sumit Rajan
    Jan 17, 2004
  2. Adam Parkin
    Replies:
    6
    Views:
    534
    Victor Bazarov
    Apr 24, 2004
  3. Xiangliang Meng
    Replies:
    1
    Views:
    1,668
    Victor Bazarov
    Jun 21, 2004
  4. Neelesh

    Friend Functions and Scope

    Neelesh, Nov 9, 2005, in forum: C++
    Replies:
    9
    Views:
    362
    Neelesh
    Nov 9, 2005
  5. Peter
    Replies:
    2
    Views:
    313
    Öö Tiib
    Jun 6, 2013
Loading...

Share This Page