Jojo said:
I'll look that up. At first glance, the C++ method seems much less
'comprehensive' than sprintf. But I think it's better for me to get rid
of my plain-C habits when coding C++
The C-style format string functions are compact, relatively easy
to learn and remember, and fast. Once you get the hang of them, it's
very easy to perform relatively complex formatting with a very small
amount of code which only takes a small amount of time to write.
Also, you never need to worry if some formatting setting "leaks" or
not (ie. if the stream will "remember" that setting or forget about
it immediately after printing the value).
The C++-style printing functions are much more verbose, and a
complex C one-liner can easily become over a dozen of lines of C++
(or, alternatively, a dozen of << operator calls in a row) using
diverse function names from <iostream> and <iomanip>. In some cases
it also feels a bit inconsistent whether some stream setting is
remembered or forgotten between << operator calls. Most are forgotten,
but some are remembered, causing potential "formatting leaks".
On the other hand:
C-style printing functions lack abstraction, which is the whole point
in the C++-style ones. For example, assume you have this:
typedef int MyIndex;
....
// Somewhere else:
MyIndex index;
....
std:

rintf("The value is: %i\n", index);
The printf() breaks the abstraction of 'MyIndex' because it assumes
it's an int. If you later change the typedef to:
typedef long MyIndex;
the printf() will break if long is larger than int (which is usually
the case in 64-bit systems). Even if the compiler is so smart as to
warn you about the incompatible printf format string, it could still
be tedious to go and change every single printf which uses that type
(which is something you wanted to avoid doing by using the typedef
in the first place).
Getting around this problem can be done, in a limited way, with all
kinds of awkward kludges, such as:
typedef int MyIndex;
#declare MyIndexFormatString "%i"
....
std:

rintf("The value is: " MyIndexFormatString "\n", index);
Of course this breaks immediately if you change MyIndex to something
not supported by printf(), such as a class.
Naturally you could try to avoid this problem by writing a function
for the only purpose of printing your MyIndex, like this:
void printMyIndex(MyIndex value);
....
std:

rintf("The value is: ");
printMyIndex(index);
std:

rintf("\n");
But this has two problems: You already lost the advantage over
the C++-way (ie. compactness), and you can't specify the formatting
for printing the value anylonger. You would have to add even more
kludges to this if you wanted to be able to specify the formatting:
void printMyIndex(MyIndex value, const char* format);
....
std:

rintf("The value is: ");
printMyIndex(index, "05");
std:

rintf("\n");
Of course if you wanted that width parameter to be defined by
a variable instead of being a string literal, it would become even
more complicated and awkward. The more you develop this further,
the more you are actually re-implementing C++ printing functions.
Also, the lack of abstraction causes a really bad breakdown with
templates. Consider:
template<typename T>
void print(const T& value)
{
std:

rintf("???", value); // What to write here???
}
The big advantage of C++-style printing is that it's abstract.
You don't have to worry about types. For example:
std::cout << index << std::endl; // I don't care what 'index' is.
Or:
template<typename T>
void print(const T& value)
{
std::cout << T << std::endl; // It doesn't matter what T is.
}
If you create your own class and you want to support printing it
in the regular way, you can! So it's perfectly possible to do this:
class MyIndex { ... };
....
MyIndex index;
....
std::cout << index << std::endl;
That MyIndex could be an int, a long, a double, a string or your
own Class, it doesn't matter. You don't have to specify the type
explicitly in the printing command, unlike in C.