What is the correct way to implement a printable class in C++ if one wants to avoid processing iostream
as much as possible?
This question raises from the interaction between iostream
, iosfwd
with templates.
1. Traditional approach
The traditional approach as far as I know is:
MyClass.hpp
#include<iostream>
template<class T>
class MyClass{
...
friend std::ostream& operator<<(std::ostream& os, MyClass const& self){...; return os;}
};
However although some classes can be printable, e.g. for debugging, it is not necessarily true that all users will be interested in printing anything.
These users will be paying (at compile time) a price for something that will never use.
That is to include and compile iostream
.
(There is iosfwd
but it is not useful for header-only.)
2. Template operator<<
template<class T>
class MyClass{
...
template<Ostream>
friend Ostream& operator<<(Ostream& os, MyClass const& self){...; return os;}
};
This one I don't have to include iostream
in the header file.
It also has the problem that operator<<
may return not standard objects, like std::ofstream
(instead of the base std::ostream
).
Users interested in printing will have to include iostream
themselves (and know this to some degree.)
Drawbacks is that Ostream
can match other things.
One can combine this with iosfwd
to constrain Ostream
(e.g. enable_if<is_base<std::ostream, Ostream>::value>
.)
Not sure if it works.
3. Separate header MyClass/io.hpp
for printing
Add a separate file
MyClass/io.hpp
#include<iostream>
template<class T>
std::ostream& operator<<(std::ostream& os, MyClass<T> const& self){...; return os;}
In this case, users will have to include MyClass/io.hpp
to print MyClass
(and know that).
Also, it may need friendship to print private members, and for that it will need <iosfwd>
in MyClass.h.
I have seen this design in boost/units/io.hpp
library.
4. Combine 2 and 3
Go full template
MyClass/io.hpp
template<class Ostream, class T>
Ostream& operator<<(Ostream& os, MyClass<T> const& self){...; return os;}
What is the recommended way to interact with std::iostream
in when using templates and header-only and avoiding compilation of iostream
if possible?
In conclusion, it seems that using separate files can avoid the inclusion of <iostream>
. Templates and without need for friendship can avoid iosfwd
as well. Restricted templates or need for friendship may still need iosfwd
.