friends, templates and comeau, gcc

Discussion in 'C++' started by werasm, Nov 30, 2009.

  1. werasm

    werasm Guest

    Hi all,

    I have this (minimal) piece of code that does not compile under comeau
    (online) and gcc 4.2.1.

    namespace n{

    template <class T> struct X
    {
    friend int foo( X ){ return 0; }
    };

    }//!n

    int main()
    {
    n::X<int> x;
    return n::foo( x );
    }

    Comeau output:

    "ComeauTest.c", line 15: error: namespace "n" has no member "foo"
    return n::foo( x );

    When removing the namespace qualification, everything compiles fine,
    therefore...
    [...as before...]
    int main()
    {
    n::X<int> x;
    return foo( x );
    }

    ....now compiles without errors.

    Changing the example slightly to here below:

    namespace n{

    template <class T> class X;

    template <class T> int foo( X<T> )
    { return 0; }

    template <class T>
    struct X
    {
    friend int foo<>( X<T> );
    };

    }//!n

    int m()
    {
    n::X<int> x;
    return n::foo( x );
    }

    Does not result in the error (irrespective of whether n is qualified
    or not). I have not checked gcc with respect to this.

    Can anybody perhaps give an explanation concerning this behaviour. Is
    this perhaps a compiler bug?

    Kind regards,

    Werner
     
    werasm, Nov 30, 2009
    #1
    1. Advertising

  2. werasm

    Balog Pal Guest

    "werasm" <>

    > I have this (minimal) piece of code that does not compile under comeau
    > (online) and gcc 4.2.1.
    >
    > namespace n{
    >
    > template <class T> struct X
    > {
    > friend int foo( X ){ return 0; }
    > };
    >
    > }//!n
    >
    > int main()
    > {
    > n::X<int> x;
    > return n::foo( x );
    > }
    >
    > Comeau output:
    >
    > "ComeauTest.c", line 15: error: namespace "n" has no member "foo"
    > return n::foo( x );


    This looks a tricky one. 11.4p5 states

    A function can be defined in a friend declaration of a class if and only if
    the class is a nonlocal 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] 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).


    What means you shouldn't see foo from main in any case. Only in member
    functions of x. I triec cameau, and you indeed can use foo there, but only
    unqualified. n::foo there gives the same error. I'm not sure whether that
    is supposed to happen or not, for practical reasons it hardly matters.


    >
    > When removing the namespace qualification, everything compiles fine,
    > therefore...
    > [...as before...]
    > int main()
    > {
    > n::X<int> x;
    > return foo( x );
    > }
    >
    > ...now compiles without errors.


    If you use ::foo in this case it is correctly reported as not there.
    If you make foo use different arguments, it is also not there.

    So it seem to be found by ADL somehow (unless really a bug) -- I leave that
    part of explanation to someone else ;-)) certainly that can only apply
    without qualification.
     
    Balog Pal, Nov 30, 2009
    #2
    1. Advertising

  3. werasm wrote:

    > Hi all,
    >
    > I have this (minimal) piece of code that does not compile under comeau
    > (online) and gcc 4.2.1.
    >
    > namespace n{
    >
    > template <class T> struct X
    > {
    > friend int foo( X ){ return 0; }
    > };
    >
    > }//!n
    >
    > int main()
    > {
    > n::X<int> x;
    > return n::foo( x );
    > }
    >
    > Comeau output:
    >
    > "ComeauTest.c", line 15: error: namespace "n" has no member "foo"
    > return n::foo( x );
    >
    > When removing the namespace qualification, everything compiles fine,
    > therefore...
    > [...as before...]
    > int main()
    > {
    > n::X<int> x;
    > return foo( x );
    > }
    >
    > ...now compiles without errors.
    >
    > Changing the example slightly to here below:
    >
    > namespace n{
    >
    > template <class T> class X;
    >
    > template <class T> int foo( X<T> )
    > { return 0; }
    >
    > template <class T>
    > struct X
    > {
    > friend int foo<>( X<T> );
    > };
    >
    > }//!n
    >
    > int m()
    > {
    > n::X<int> x;
    > return n::foo( x );
    > }
    >
    > Does not result in the error (irrespective of whether n is qualified
    > or not). I have not checked gcc with respect to this.
    >
    > Can anybody perhaps give an explanation concerning this behaviour. Is
    > this perhaps a compiler bug?
    >


    This is how it is supposed to work. You have to differentiate:

    - Introducing a name into a declarative region.
    - Introducing a *visible* name into a declarative region.
    - Putting a declaration directly into a declarative region.

    Visibility is a property of names that make them visible to unqualified name
    lookup (a name can be hidden when doing "inside out" lookup, from nested
    regions to outer regions).

    Normally, declarations introduce visible names into a declarative region
    (namespace body, class body, ...). Certain declarations introduce names, but
    the name is not visible when looked up in that declarative region:

    void f() { extern int a; /* visible here */ }
    void g() { a; /* error: 'a' not visible */ }
    void h() {
    extern float a; /* error: a introduced as int variable already */
    }
    extern float a; /* same error */
    extern int a; /* alright, now it's visible ("in scope") from here */

    A friend declaration acts similar, as it introduces a name not into the
    region the declaration appears in, but into the nearest enclosing namespace
    scope, as a not visible name:

    struct A { friend void f(); friend void g(); };
    int main() { f(); } // error: f not visible
    int g(); // error: void g(); and int g(); not overloadable

    That was unqualified name lookup without considering ADL (argument dependent
    lookup). ADL says:

    "When an unqualified name is used as the postfix-expression in a function
    call (5.2.2), other namespaces not considered during the usual unqualified
    lookup (3.4.1) may be searched, and namespace-scope friend function
    declarations (11.4) not otherwise visible may be found."

    You may wonder how it knows "f()" is a function call, even though it doesn't
    yet know that "f" is a function: This is because every other syntax that
    matches "f()" is constrained: "f" needs to be a type name, for instance (see
    3.4.1/3). But as a function call, we don't need constraints. Now, the friend
    declaration within the class' declarative region that declared a namespace
    scope function is considered in addition and is visible to ADL, since a
    function argument of your call was a class that contained that declaration.

    That was the unqualified case. If you consider qualified-lookup into a
    namespace, like "n::f", then it will lookup in declarations that occured in
    that namespace (and as a special case, considering using-directives too
    ("using namespace ...")). However, your friend declaration appeared not in
    that namespace, and you did not have an explicit declaration of it in that
    namespace. Thus, there is no declaration found.
     
    Johannes Schaub (litb), Nov 30, 2009
    #3
  4. werasm

    werasm Guest

    On Nov 30, 4:21 pm, "Johannes Schaub (litb)" <>
    wrote:

    > This is how it is supposed to work. You have to differentiate:
    >
    > - Introducing a name into a declarative region.
    > - Introducing a *visible* name into a declarative region.
    > - Putting a declaration directly into a declarative region.


    OK


    > A friend declaration acts similar, as it introduces a name not into the
    > region the declaration appears in, but into the nearest enclosing namespace
    > scope, as a not visible name:


    Therefore:

    Defining a friend inside a class introduces the name into the
    declarative region of the nearest inclosing namespace scope, but as an
    invisible name that is only found by ADL (and cannot be found when
    qualified).

    Do you have any idea why they would want to make it invisible (in the
    enclosing namespace scope) in the first place (as a matter of
    interest)? i.e. Is it beneficial in any way to have it invisible?

    Thank you for your explanation and kind regards,

    Werner
     
    werasm, Nov 30, 2009
    #4
  5. werasm

    James Kanze Guest

    On Nov 30, 11:58 am, werasm <> wrote:

    > I have this (minimal) piece of code that does not compile
    > under comeau (online) and gcc 4.2.1.


    > namespace n{


    > template <class T> struct X


    I don't think the fact that X is a template is relevant here. I
    think you'll have the same problem with an ordinary class.

    > {
    > friend int foo( X ){ return 0; }
    > };
    > }//!n


    > int main()
    > {
    > n::X<int> x;
    > return n::foo( x );
    > }


    > Comeau output:


    > "ComeauTest.c", line 15: error: namespace "n" has no member "foo"
    > return n::foo( x );


    A better message would be ``namespace "n" has no visible member
    "foo"''. Namespace n clearly has a member foo, but it's not
    been declared in a scope which would make it visible here.

    > When removing the namespace qualification, everything compiles fine,
    > therefore...
    > [...as before...]
    > int main()
    > {
    > n::X<int> x;
    > return foo( x );
    > }


    Yep. When you say n::..., you're telling the compiler
    explicitly to look at what is visible in n (which doesn't
    necessarily contain everything that is in n). When you leave
    the qualification off, you're telling the compiler to look at
    what is normally visible at the call site, *and* at any
    additional scopes introduced by ADL. In this case, ADL
    introduces the scope of n::X, and in the scope of n::X, there is
    a declaration which makes n::foo visible.

    > ...now compiles without errors.


    > Changing the example slightly to here below:


    > namespace n{


    > template <class T> class X;


    > template <class T> int foo( X<T> )
    > { return 0; }


    > template <class T>
    > struct X
    > {
    > friend int foo<>( X<T> );
    > };
    > }//!n


    > int m()
    > {
    > n::X<int> x;
    > return n::foo( x );
    > }


    > Does not result in the error (irrespective of whether n is
    > qualified or not). I have not checked gcc with respect to
    > this.


    > Can anybody perhaps give an explanation concerning this
    > behaviour. Is this perhaps a compiler bug?


    No bug, although probably not very intuitive. Just because
    something exists in a given namespace doesn't mean that it is
    visible in a given scope. A declaration must be visible in that
    scope for it to be visible. A friend declaration isn't in the
    surrounding namespace scope, but rather in the class, so it
    only makes the symbol visible in the class scope. In your last
    example, you have a declaration in the scope itself, so the
    symbol becomes visible anytime lookup considers the scope.

    --
    James Kanze
     
    James Kanze, Nov 30, 2009
    #5
    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. Buster Copley
    Replies:
    5
    Views:
    561
    Gianni Mariani
    Jul 7, 2003
  2. Replies:
    2
    Views:
    600
  3. Rasmus Johansen
    Replies:
    4
    Views:
    382
    Victor Bazarov
    Oct 18, 2007
  4. Replies:
    0
    Views:
    658
  5. Johannes Schaub (litb)

    Weird difference between comeau and gcc/clang

    Johannes Schaub (litb), Oct 13, 2010, in forum: C++
    Replies:
    15
    Views:
    597
    James Kanze
    Oct 18, 2010
Loading...

Share This Page