The standard input and output stream classes (std::basic_istream
and std::basic_ostream
) overload member and non-member operator>>()
and operator<<()
functions. Those are the functions that perform the actual I/O. Manipulators are functions like std::endl
, std::left
, std::right
, std::boolalpha
, etc. They merely complement the functionality provided by the I/O operator functions. They allow us to control certain properties of the I/O operation like floating point representation, field width, fill characters, and so on.
In regards to the example you provided:
std::cout << "static constructor\n";
This calls the non-member function ostream& operator<<(ostream&, const char*);
which does the actual output of the string.
A manipulator can be defined like this:
std::ostream& print_1_to_10( std::ostream& os ) {
for (int i = 1; i <= 10; i++) {
os << to_string(i);
}
}
The standard stream classes have member overloads of operator>>()
and operator<<()
which take as an argument a pointer to a function with the above signature. Basically it goes like:
std::ostream& std::ostream::operator<<(std::ostream&(*pf)(std::ostream&)) {
pf(*this);
return *this;
}
This allows us to do:
std::cout << print_1_to_10;
which is what allows manipulators to work.
So in summary, manipulators are simply helper functions that facilitate certain kinds of operations.
There is also the case where you might want something that resembles a manipulator, but in addition you want to pass arguments to this manipulator (i.e std::cout << print_1_to(10)
). In that case it is customary to create a class that contains an overloaded operator<<()
or operator>>()
depending on what you want to do.
struct print_1_to {
int n;
print_1_to(int n) : n(n) {}
friend std::ostream& operator<<(std::ostream& os, print_1_to const& p) {
for (int i = 1; i <= p.n; i++) {
os << to_string(i);
}
return os;
}
};