Friends again... (no templates :)

Discussion in 'C++' started by Andrey Tarasevich, Feb 1, 2007.

  1. Hello

    Consider the following code fragment

    namespace N {
    struct S {
    void foo();
    };
    }

    using namespace N; // 1

    class C {
    friend struct S; // 2
    C() {}
    };

    void N::S::foo() {
    C c; // 3 Is C's constructor accessible here???
    }

    According to what I can see in the standard, friend declaration at point
    2 is supposed to perform name lookup for unqualified name 'S' in order
    to determine whether this is the first declaration of 'S'. According to
    7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
    (which happens to be the global scope in this case). According to
    3.4.1/2, name 'S' introduced by a using directive at point 1 is
    considered to be a member of global namespace for the purposes of
    unqualified name lookup. This makes me to conclude that the friend
    declaration at 2 is supposed to refer to 'N::S' instead of introducing a
    new [hidden] declaration of 'struct S' into the global namespace. I.e.
    the constructor of 'C' is supposed to be accessible at point 3.

    Experiments show that GCC 3.3.3 accepts the code. So does MSVC++ 2005
    (for what it's worth). But Comeau Online rejects it, complaining that
    the constructor is inaccessible at 3. I just found out that GCC 4.1 also
    refuses to compile this code for the same reason.

    Did something change in the language specification recently? Something
    that applies in this case?

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Feb 1, 2007
    #1
    1. Advertising

  2. Andrey Tarasevich

    Greg Herlihy Guest

    On 2/1/07 3:06 PM, in article , "Andrey
    Tarasevich" <> wrote:

    > Consider the following code fragment
    >
    > namespace N {
    > struct S {
    > void foo();
    > };
    > }
    >
    > using namespace N; // 1
    >
    > class C {
    > friend struct S; // 2
    > C() {}
    > };
    >
    > void N::S::foo() {
    > C c; // 3 Is C's constructor accessible here???
    > }
    >
    > According to what I can see in the standard, friend declaration at point
    > 2 is supposed to perform name lookup for unqualified name 'S' in order
    > to determine whether this is the first declaration of 'S'. According to
    > 7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
    > (which happens to be the global scope in this case). According to
    > 3.4.1/2, name 'S' introduced by a using directive at point 1 is
    > considered to be a member of global namespace for the purposes of
    > unqualified name lookup. This makes me to conclude that the friend
    > declaration at 2 is supposed to refer to 'N::S' instead of introducing a
    > new [hidden] declaration of 'struct S' into the global namespace. I.e.
    > the constructor of 'C' is supposed to be accessible at point 3.


    A using directive like the one above ("using namespace N") does not
    "introduce" any names into the declarative region in which the directive
    appears. Instead, a using directive merely affects how names are looked up
    within that declarative region.

    Since a friend declaration requires that the named friend (S in this case)
    be a member of the enclosing declarative region (if the friend is to be
    found there), a using directive is not enough in this case for the compiler
    to find the intended friend class: N::S.

    Since a using declaration does introduce a name into its enclosing region,
    it is possible to fix this problem by using a using declaration instead of
    using a using directive to bring the name S into the proper declarative
    region. For example:

    using N::S; // 1

    class C
    {
    friend struct S; // 2
    C() {}
    };

    Greg
    Greg Herlihy, Feb 2, 2007
    #2
    1. Advertising

  3. Andrey Tarasevich

    Sumit Rajan Guest

    Andrey Tarasevich wrote:
    > Hello
    >
    > Consider the following code fragment
    >
    > namespace N {
    > struct S {
    > void foo();
    > };
    > }
    >
    > using namespace N; // 1
    >
    > class C {
    > friend struct S; // 2


    public:

    > C() {}
    > };
    >
    > void N::S::foo() {
    > C c; // 3 Is C's constructor accessible here???
    > }


    Comeau should now be able to compile it.

    Regards,
    Sumit.
    Sumit Rajan, Feb 2, 2007
    #3
  4. Andrey Tarasevich

    Sumit Rajan Guest

    Sumit Rajan wrote:
    > Andrey Tarasevich wrote:
    >> Hello
    >>
    >> Consider the following code fragment
    >>
    >> namespace N {
    >> struct S {
    >> void foo();
    >> };
    >> }
    >>
    >> using namespace N; // 1
    >>
    >> class C {
    >> friend struct S; // 2

    >
    > public:
    >
    >> C() {}
    >> };
    >>
    >> void N::S::foo() {
    >> C c; // 3 Is C's constructor accessible here???
    >> }

    >
    > Comeau should now be able to compile it.



    Oops! Should've paid more attention. Please disregard my previous post.

    Sumit.
    Sumit Rajan, Feb 2, 2007
    #4
  5. Greg Herlihy wrote:
    >
    >> Consider the following code fragment
    >>
    >> namespace N {
    >> struct S {
    >> void foo();
    >> };
    >> }
    >>
    >> using namespace N; // 1
    >>
    >> class C {
    >> friend struct S; // 2
    >> C() {}
    >> };
    >>
    >> void N::S::foo() {
    >> C c; // 3 Is C's constructor accessible here???
    >> }
    >>
    >> According to what I can see in the standard, friend declaration at point
    >> 2 is supposed to perform name lookup for unqualified name 'S' in order
    >> to determine whether this is the first declaration of 'S'. According to
    >> 7.3.1.2/3, the lookup goes up to the innermost enclosing namespace scope
    >> (which happens to be the global scope in this case). According to
    >> 3.4.1/2, name 'S' introduced by a using directive at point 1 is
    >> considered to be a member of global namespace for the purposes of
    >> unqualified name lookup. This makes me to conclude that the friend
    >> declaration at 2 is supposed to refer to 'N::S' instead of introducing a
    >> new [hidden] declaration of 'struct S' into the global namespace. I.e.
    >> the constructor of 'C' is supposed to be accessible at point 3.

    >
    > A using directive like the one above ("using namespace N") does not
    > "introduce" any names into the declarative region in which the directive
    > appears.


    Yes, it does "virtually" introduce the names into the region
    ("virtually" here means that the names are introduced for the purpose of
    name lookup only). That is exactly what 3.4.1/2 says:

    "For the purpose of of the unqualified name lookup rules described in
    3.4.1, the declarations from the namespace nominated by the
    'using-directive' are considered members of that enclosing namespace."
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    > Instead, a using directive merely affects how names are looked up
    > within that declarative region.


    Well, that is exactly _how_ in affects the lookup process. By
    introducing the names into the region _as_ _members_ (for the purposes
    of unqualified name lookup only).

    > Since a friend declaration requires that the named friend (S in this case)
    > be a member of the enclosing declarative region (if the friend is to be
    > found there), a using directive is not enough in this case for the compiler
    > to find the intended friend class: N::S.


    The standard does not agree with you on that. Once again, it literally
    says that in the above example for the purposes of unqualified name
    lookup name 'S' (standing for 'N::S') is considered a member of global
    namespace.

    > Since a using declaration does introduce a name into its enclosing region,
    > it is possible to fix this problem by using a using declaration instead of
    > using a using directive to bring the name S into the proper declarative
    > region. For example:
    >
    > using N::S; // 1
    >
    > class C
    > {
    > friend struct S; // 2
    > C() {}
    > };


    This will cause at least Comeau Online to issue a different error - at
    point 2 it will say that name 'S' has already been declared in this
    scope. It is the same problem as in

    namespace N { struct S {}; }

    using N::S;
    struct S {}; // ERROR: 'S' has already been declared

    Apparently, in your modified example Comeau still refuses to link friend
    declaration to 'N::S'. I might look into that separately later, but at
    this time I'm primarily interested in the original version with using
    directive.

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Feb 2, 2007
    #5
  6. Andrey Tarasevich wrote:
    > ...
    > Did something change in the language specification recently? Something
    > that applies in this case?
    > ...


    OK, it looks like it all comes from here

    http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#138
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1229.html

    It all still appears to be in the drafting stage, but according to the
    recommendations section in 'n1229' using directives should not be
    considered by name lookup performed for friend declarations. In other
    words, Comeau Online and GCC 4.1 do indeed violate the requirements of
    the current language specification by implementing on of the possible
    future changes in it (a bit prematurely, I'd say).

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Feb 2, 2007
    #6
  7. Andrey Tarasevich wrote:
    > ...


    It looks like the situation with correct support of "friends" is pretty
    grim even when it comes to simpler cases. In the following code

    struct B { void foo(); };

    namespace X {
    class A {
    A() {}
    friend struct B; // refers to X::B, not to ::B
    };

    struct B {
    void foo();
    };
    }

    void X::B::foo() {
    X::A a; // OK
    }

    void B::foo() {
    X::A a; // ERROR
    }

    compilers are supposed to accept the 'OK' line and reject the 'ERROR'
    one. In practice so far I only found that Comeau Online and Forte
    Developer 7 C++ 5.4 behave properly. MS C++ compilers (including 2005),
    GCC 3 get it wrong. And that despite the fact that the standard even has
    a specific example dedicated to this situation in 7.3.1.2/3...

    --
    Best regards,
    Andrey Tarasevich
    Andrey Tarasevich, Feb 2, 2007
    #7
    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. Kevin Christopher

    templates and friends

    Kevin Christopher, Jul 1, 2003, in forum: C++
    Replies:
    3
    Views:
    340
    tom_usenet
    Jul 2, 2003
  2. JKop
    Replies:
    3
    Views:
    451
  3. Replies:
    2
    Views:
    589
  4. recover
    Replies:
    2
    Views:
    786
    recover
    Jul 25, 2006
  5. Replies:
    0
    Views:
    645
Loading...

Share This Page