friends, templates and comeau, gcc

W

werasm

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
 
B

Balog Pal

werasm said:
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.
 
J

Johannes Schaub (litb)

werasm said:
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.
 
W

werasm

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
 
J

James Kanze

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top