well, I don't know if it does work with all the compilers. It works on
vc8, g++ 4.1 and g++ 3.4 for sure, because I tried it. It declares only
the relevant function as friend.
The reason I ask is that all of this is related to how friend
declarations are handled, and the C++ standard changed this with
regards to what was generally being done before. Which means
that I would have some doubts if you had to support older
compilers. (Both g++ 4.1 and VC++ 8 are very up to date, and
g++ 3.4 is fairly up to date in this respect as well. Where I
work, however, I have to support older versions of Sun CC, and
the Windows programmers still use VC++ 6. Regretfully, I fear
that this situation is rather widespread.)
In my opinion your solution is much more elegant. You can also avoid
friend if you put your print public, given that is a safe function (even
if it complicates without reason the public interface of the class).
No you can't
. My solution requires that the operator be
defined inline, in the class body, and the only way you can
define a non member function in a class body is to declare it
friend. In this case, friendship isn't about giving access
rights.
In practice, I have a class template, IOStreamOps, which looks
about like:
template< typename T >
struct IOStreamOps
{
friend std:
stream&operator<<( std:
stream& dest, T const&
source )
{
source.print( dest ) ;
return dest ;
}
friend std::istream&operator>>( std::istream& source, T&
dest )
{
dest.scan( source ) ;
return source ;
}
} ;
If I want to provide IO in a class, I'll derive from the
appropriate instantiation, and define a print (and/or scan)
function in the class, e.g.:
class MyClass : public IOStreamOps< MyClass >
{
public:
// ...
void print( std:
stream& ) ;
} ;
(I actually started doing this as a means of getting all of the
binary operators: given +=, -=, etc., deriving from
ArithmeticOps<> gives my +, -, etc. I believe that the idea
originally comes from Barton and Nackman, but it was Michel
Michaud who suggested it to me.)
Note that this works both with older compilers, and with newer
ones, but for different reasons. In traditional, pre-standard
C++, a function declared as a friend was "injected" into the
surrounding namespace (except that in traditional C++, it wasn't
"namespace", but file scope), and so would be found by normal
lookup. The C++ standard suppressed this injection, but ADL
will look inside the relevant classes, and finds the name.
Also, I'm not sure what you'd call a function like this.
Formally, it's not a function template, but a normal, free
function. But it gets instantiated exactly like a template
function; in fact, with regards to templates, it behaves exactly
like a (static) member function of the class template. Except,
of course, that it isn't a member.
Usually in my case, when the classes haven't got the getters for all the
parameters that I want to show (that is not so common), I go for a
solution similar to yours, and if the performances are not a problem and
it makes sense, I create a 'std::string toString()' public method
without friend that will be called by operator<<.
Interesting. I generally do the opposite: the actual conversion
is in the print() function, and the toString() function uses
std:
stringstream to generate the string, e.g.:
std::string
MyClass::asString() const
{
std:
stringstream result ;
print( result ) ;
return result.str() ;
}
Practically speaking, as soon as the class contains any numeric
types, or containers, or any user defined class that supports
<< (but not asString()), I need the ostringstream anyway. So I
might as well use it up front (and be sure of only using one).