Topic: Defining friend functions within classes - final CD text seems to contradict the example
Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1998/09/21 Raw View
"Brad Daniels" <brad.daniels@missioncritical.com> writes:
> Anyway, you seem to be saying that that text says nothing about where the
> function is actually defined, yes? The old behavior of MSVC5.0 was that it
> appeared to define the function as a global, even when the class that
> defined it was in a namespace. Is that the right behavior, or should it be
> defined in the enclosing namespace, or is it unspecified in the absence of a
> disambiguating forward declaration?
No, it sould be defined always in the enclosing namespace. Since VC++
does not implement Koenig lookup, putting it in the global namespace
is a good approximation.
> I know because it doesn't find the function when I try to call it from
> outside the class.
A friend function not declared in namespace scope is not supposed to
be found during normal name lookup (in particular, not by qualified
lookup). Only Koenig lookup will find it.
For a non-inline function, you can tell the scope where it is defined
by implementing it in different scopes, and then checking which
versions link.
For inline friend functions, I believe you can't distinguish the
scenarios
a) the function is not visible at all outside the class, and Koenig
lookup is not implemented, and it is in the global namespace
b) the function is not visible at all outside the class, and Koenig
lookup is not implemented, and it is in the enclosing namespace
You can try whether it accepts qualified calls to the friend (which it
shouldn't), and you can try whether it accepts unqualified calls in
the namespace of the class.
If you can't call it at all from outside the class, this is a bug, but
you don't know into which namespace it placed the hidden definition.
> Maybe I should rephrase the question. Does the friend declaration declare a
> function, and if so, what function does it declare, ::op<< or
> X::op<<?
X::op<<.
> The text was making it sound like it should declare X::T::op<<
No. The example in 7.3.1.1 has this explicit scenario and shows that a
friend function should not be qualified with the class.
Regards,
Martin
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1998/09/17 Raw View
"Daniels, Brad" <Brad.Daniels@missioncritical.com> writes:
> A fried 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)."
There are two issues here: What lookup rules apply for names inside
the function, and how can I find the function itself. The 'lexical
scope' thing answers the first question, more clearly described in
[basic.scope.namespace]/9
>> Name lookup for a name used in the definition of a friend function
>> (11.4) defined inline in the class granting friendship shall
>> proceed as described for lookup in member function definitions. If
>> the friend function is not defined in the class granting
>> friendship, name lookup in the friend function definition shall
>> proceed as described for lookup in namespace member function
>> definitions.
As a result, you can say
class X{
static int v;
friend void f(){
v=1;
}
};
but you can't say
class X{
static int v;
friend void f()
};
void X::f(){
v=1; //v not declared
}
> VC6.0 changed the behavior, however, so that f is now effectively an inline
> static member function of class M.
How do you know? There is more to friend functions: Koenig lookup. A
friend function not declared in namespace scope is not found during
normal name lookup, but Koenig lookup will find it.
> namespace X {
>
> template <class C> class T {
> //... public:
> friend ostream& operator<<(ostream& s, const T<C>& ob);
>
> void f(ostream &s) { s << *this; }
> };
>
> }
>
> Which op<< does T::f invoke? Is it X::op<< or ::op<<?
Normal lookup will find ::op<<, Koenig lookup will find X::op<< in
addition, since it searches hidden friends defined in T's namespace.
Overload resolution will select the best match, probably X::op<<.
Hope this helps,
Martin
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Brad Daniels" <brad.daniels@missioncritical.com>
Date: 1998/09/17 Raw View
Martin von Loewis wrote in message ...
>"Daniels, Brad" <Brad.Daniels@missioncritical.com> writes:
>> A fried 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)."
>
>There are two issues here: What lookup rules apply for names inside
>the function, and how can I find the function itself. The 'lexical
>scope' thing answers the first question, more clearly described in
>[basic.scope.namespace]/9
Thanks for replying... I was beginning to fear no one cared. :-)
Anyway, you seem to be saying that that text says nothing about where the
function is actually defined, yes? The old behavior of MSVC5.0 was that it
appeared to define the function as a global, even when the class that
defined it was in a namespace. Is that the right behavior, or should it be
defined in the enclosing namespace, or is it unspecified in the absence of a
disambiguating forward declaration?
I used this trick primarily for operators, though, so I can't be sure it
didn't just trick the rest of the compiler into handling operator overloads
properly. I do know that VC5 would't find op<< in this case:
namespace N {
class X {
...
friend ostr & operator<<(ostr&,const X&);
};
ostr & operator<<(ostr&,const X&);
}
Forward declarations of op<< inside namespace N would make no difference.
If I made the op<< global, or made it an inline friend definition in X,
however, it worked. From this I inferred that it was making inline friend
functions global.
...
>> VC6.0 changed the behavior, however, so that f is now effectively an inline
>> static member function of class M.
>
>How do you know?
I know because it doesn't find the function when I try to call it from
outside the class. It sounds like the real problem is that MSVC6 still
doesn't get Koenig lookup right, but I'd still like to understand what's
really supposed to happen, and I'd like to know if the other aspects of
their handling of these cases is confromant. I'm also trying to identify
the bug or bugs as precisely as possible before reporting it to Microsoft,
since fuzzy bug reports don't generally get fixed in a timely manner.
...
>
>> namespace X {
>>
>> template <class C> class T {
>> //... public:
>> friend ostream& operator<<(ostream& s, const T<C>& ob);
>>
>> void f(ostream &s) { s << *this; }
>> };
>>
>> }
>>
>> Which op<< does T::f invoke? Is it X::op<< or ::op<<?
>
>Normal lookup will find ::op<<, Koenig lookup will find X::op<< in
>addition, since it searches hidden friends defined in T's namespace.
>Overload resolution will select the best match, probably X::op<<.
Maybe I should rephrase the question. Does the friend declaration declare a
function, and if so, what function does it declare, ::op<< or X::op<<? The
text was making it sound like it should declare X::T::op<<, which would be
weird, but your explanation that it applies only to name resolution within
the definition sounds right. My compiler will generate code for an
invocation of X::T::f even if there is no other declaration of op<<. It
appears to generate a reference to X::op<<, but I can't find any text that
says whether it should.
I've been examining the compiler's behavior more closely in the course of
writing this note, and I've discovered that all the trivial examples I've
come up with that should be equivalent to the one that doesn't work do in
fact work. In some of my production code, MSVC++ could not find my op<<
when it was defined as an inline friend, but when I explicitly declared it
at global scope and moved the implementation to my .cpp file, it worked. I
have tried generating a trivial example to show the bad behavior, but I
can't. Obviously there's a more subtle bug hiding here.
- Brad
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Daniels, Brad" <Brad.Daniels@missioncritical.com>
Date: 1998/09/15 Raw View
Here goes try 3.... It appears my newsreader may have thought the group was
unmoderated, thus sending out the message without approval. Apologies if
you've seen this message before.
I have the "Final CD", so it's possible an editorial change was made to
clear this problem up:
In section 11.4, paragraph 5, the text says: "A function can be defined in
a friend declaration of a class iff the class is a non-local class, the
function name is unqualified, and the function has namespace scope.
[example excerpted below] Such a function is implicitly inline. A fried
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)."
The phrasing of the above is a bit confusing, so I looked at the example:
class M {
friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
The comment in the example is pretty unambiguous, and provides some useful
behavior. MicroSoft's VC5.0 compiler provided behavior compatible with the
above example, and I used it to get around limitations in their template
support. (I used it to define global insertion and extraction operators for
a template class.)
VC6.0 changed the behavior, however, so that f is now effectively an inline
static member function of class M. Reading the text again, it seems that
this behavior may be correct. What happened here? I found the feature as
described in the example tremendously useful for defining two-operand
operator overloads. I also have no idea how I'd get the following to work
any more:
namespace X {
template <class C> class T {
//... public:
friend ostream& operator<<(ostream& s, const T<C>& ob) { /*...*/ }
};
}
I think the behavior I was using was actually a bug, in that the operator
should probably have been at namespace scope, not global scope. The text
quoted above, however, is not really clear as to what the following would
mean:
namespace X {
template <class C> class T {
//... public:
friend ostream& operator<<(ostream& s, const T<C>& ob);
void f(ostream &s) { s << *this; }
};
}
Which op<< does T::f invoke? Is it X::op<< or ::op<<? Section 11.4 just
says a friend function defined outside of the class is not in the scope of
the class. Does that mean it's in the enclosing namespace scope, or in the
global scope? A forward declaration of op<< at global scope would
disambiguate the reference, it seems, but that requires a forward
declaration of X::T, which didn't work when I initially wrote the code (and
may still not work; I haven't tried it yet)... The section on template
friends didn't offer any additional help.
Another issue here is the whole scope resolution thing. If I have an object
of type X::T<C>, should it matter whether the op<< is at namespace or global
scope? I seem to recall reading that the compiler is supposed to look in
the scope of the object for operators on the object, but I can't remember
where I saw it. Should it also find T<C>::op<<?
- Brad
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Brad Daniels" <brad.daniels@missioncritical.com>
Date: 1998/09/10 Raw View
My first attempt (yesterday) to submit this post seems to have fallen into
the bit bucket, so here's my second try:
I have the "Final CD", so it's possible an editorial change was made to
clear this problem up:
In section 11.4, paragraph 5, the text says: "A function can be defined in
a friend declaration of a class iff the class is a non-local class, the
function name is unqualified, and the function has namespace scope.
[example excerpted below] Such a function is implicitly inline. A fried
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)."
The phrasing of the above is a bit confusing, so I looked at the example:
class M {
friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
The comment in the example is pretty unambiguous, and provides some useful
behavior. MicroSoft's VC5.0 compiler provided behavior compatible with the
above example, and I used it to get around limitations in their template
support. (I used it to define global insertion and extraction operators for
a template class.)
VC6.0 changed the behavior, however, so that f is now effectively an inline
static member function of class M. Reading the text again, it seems that
this behavior may be correct. What happened here? I found the feature as
described in the example tremendously useful for defining two-operand
operator overloads. I also have no idea how I'd get the following to work
any more:
namespace X {
template <class C> class T {
//...
public:
friend ostream& operator<<(ostream& s, const T<C>& ob) { /*...*/ }
};
}
I think the behavior I was using was actually a bug, in that the operator
should probably have been at namespace scope, not global scope. The text
quoted above, however, is not really clear as to what the following would
mean:
namespace X {
template <class C> class T {
//...
public:
friend ostream& operator<<(ostream& s, const T<C>& ob);
void f(ostream &s) { s << *this; }
};
}
Which op<< does T::f invoke? Is it X::op<< or ::op<<? Section 11.4 just
says a friend function defined outside of the class is not in the scope of
the class. Does that mean it's in the enclosing namespace scope, or in the
global scope? A forward declaration of op<< at global scope would
disambiguate the reference, it seems, but that requires a forward
declaration of X::T, which didn't work when I initially wrote the code (and
may still not work; I haven't tried it yet)... The section on template
friends didn't offer any additional help.
Another issue here is the whole scope resolution thing. If I have an object
of type X::T<C>, should it matter whether the op<< is at namespace or global
scope? I seem to recall reading that the compiler is supposed to look in
the scope of the object for operators on the object, but I can't remember
where I saw it. Should it also find T<C>::op<<?
- Brad
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]