(e-mail address removed)>, (e-mail address removed)
says...
[ ... ]
It depends. Both declare exactly the same function, with
exactly the same linkage. But in the second case, the scope of
the declaration is the class, and it will only be found when the
compiler does a name lookup which includes the class (either
because it is from a class member function, or due to ADL).
That's how things initially look, but it's not really the
case. A friend is always at namespace scope, regardless of
where the declaration is physically placed.
There are two separate issues at hand. The first is where the
function is declared to exist---in the case of a friend, the
nearest enclosing namespace scope. The second is the scope of
the name being declared. And foo_func() in the second
example above has class scope. The name will only be found when
lookup includes that class scope, although it the name declares
a function in namespace scope.
This is a change in standard C++ with regards to classical C++.
In classical C++, the name of the friend was injected into the
enclosing file scope (classical C++ didn't have namespaces, but
it comes out to about the same). I forget the exact reasons,
but this caused some problems. And the only use of this feature
that the committee could find was the Barton and Nackman trick
(more or less what you explain below), which was covered by ADL
(which wasn't present in classical C++ either).
In fact, you can even define the function inside of the class
definition, without it having any effect on scope. For
example, at least as I read things, the following code is well
formed:
class X {
friend void f() { std::cout << "found"; }
};
int main() {
f();
return 0;
}
I don't think so. Nor do recent versions of g++: 3.2.3 accepts
it, but 4.1.0 complains: 'f' was not declared in this scope.
The situation is somewhat strange; it is the only situation I
know of where a name is declared in a specific scope, but is
introduced into a different scope. This doesn't affect the
declarative region in which the name is visible, but it does
affect where the name itself resides---a declaration of void f()
outside the class refers to the same void f() that you defined
as a friend in the class.
Change f() to take an X somehow as an argument, however, and ADL
will find the function. I regularly use something like:
template< typename T >
class ArithmeticOperators
{
friend T const operator+( T const& lhs, T const& rhs )
{
T result( lhs ) ;
result += rhs ;
return result ;
}
// And so on, for all of the operators for which there
// exists an <op>=.
}
To get the full set of operators, all I do is:
class Whatever : public ArithmeticOperators< Whatever >
{
public:
Whatever& operator+=( Whatever const& other ) ;
// And so on...
} ;
Saves a lot of typing.
Note that in this case, the friendship isn't used to access any
private members (there aren't any); it's only used to allow
defining a non-member function in the class. Similarly,
inheritance isn't used for any isA relationship; but only
because ADL pulls in not only the class, but any base classes as
well.