iostreams are horrible to use, especially if you want to format your output and they blow up compilation to a mythical degree. People go to great lengths to avoid them and I agree with them.
print/println are based on the fmt library (https://github.com/fmtlib/fmt), which had it's first release in 2015, so it's roughly as old as Rust afaik. It's mainly inspired by Python formatting.
Having per-type formatters is just a logical conclusion of having type-safe formatting.
iostreams are for all kinds of io (which includes stdin/stdout), while fmt is entirely dedicated to formatting strings. Those things are related, but not the same. cout and cerr and such will probably be entirely superseeded by print/println (I hope), but it doesn't make iostreams generally redundant.
println adds a newline and you want to be able to choose, so there is print and println.
Most of those benefits apply to std::format which was already introduced in c++20. But formatting a string you will often want to output it somewhere. You could do `std::cerr << std::format(....)`, but that just invites weird mixes of std::format and iostream based formatting. I look at print/println as partially just convenience function combing the existing std::format functionality with outputing to something. Not sure if standard permits it but print could also allow skipping some temporary heap allocated std::string which the std::format returns by directly writing to the output stream buffer.
In C++20 if you wanted to print using std::format style formating (or it's variant) your options where:
```
std::cout << std::format("{}{}", arg1, arg2);// not the best syntax but less bad than everything else, slightly inefficient due to temp string
std::string tmp = std::format("{} {}", arg1, arg2);
fwrite(stdout, tmp.c_str(), tmp.length()); // more ugly, and still uses temporary string
std::format_to(std::ostream_iterator<char>(std::cout), "{} {}", arg1, arg2); // avoids temp string, but what kind of monstrosity is this
```
But doesn't the std::format style formatting make the formatting part of ostream redundant -> it somewhat does. I guess that's why there are 3 types of print overloads:
* accepting ostream as first argument
* accepting c style "FILE*" as first output argument
* no output stream argument, implicitly outputting to stdout
One potential consideration to use ostream based versions instead of FILE* ones even though the formatting part is somewhat redundant, is RAII based resource management. If you want to use FILE* based versions, it means that you have to either remember manually close the File* handle which just like manual new/delete or malloc/free calls is something modern C++ tries to move away, or you have to create your own RAII wrappers around c style FILE* handles.
An alternative would have been introducing new kind of output stream API which only concerns with outputing buffered stream of bytes and skips the formatting part of ostream, but that would have introduced different kind of redundancy -> having 3 kinds of API for opening and writing to file. Allowing to pass ostream to print, while discouraging use of << operator parts of it seems like a simpler solution.
One more concern is how using the version of print which outputs to stdout without taking output File or ostream handle interacts with any buffering done by previous printf,and std::cout APIs and also synchronization with input streams. The same problem already existed before for interactions between printf and std::cout. For the most part if you don't mix them it doesn't matter too much, but if you care the standard defines how exactly they are synchronized. The new print probably either adds third case to this or is defined as being equivalent to one of the previous two.
After looking reading docs a bit more, seems like std::basic_streambuf/std::file_buf did exist. The new print API, might have used that as abstraction around output streams without some of the ostream formatting baggage. I have only seen them mentioned as implementation details of ostream stuff, never seen anyone use them directly.
There was also std::format_to in c++20 which avoid the temporary string std::format returns. I guess they could have extended that with additional overloads so that it can function more similar to std::print. But if they need to define new functions might as well call them something more familar to people coming from other languages like print, instead of having format_to(stdout, "formatstring", arg1, arg2);. Currently std::format_to outputs to output iterator.
So to sumarize why print exists:
- combine std::format introduced by C++20 with outputting somewhere with cleaner syntax compared to doing it manually.
- sane looking hello world for beginner and people coming from other programming languages, this is probably also why println exists
- (maybe) avoid some heap allocations that temporary string returned by std::format would cause.
- avoid confusion of mixing two formatting approaches that `std::cout<<std::format()` implies
- avoid C style manual resource management that would be required for doing `File\* f=fopen("");auto tmp=std::format();write(f, tmp.c_str())`
I was too. Looking at [0], I'm guessing it's meant to bring these benefits over old-school "printf":
- Leverages the type system to avoid mismatches between the format string and the arguments.
- Adds Python- / Rust-style support for position-based substitutions, e.g. "{1}".
- Like Rust (I think; I'm still learning Rust) you can specify per-type formatters.
But I'm still confused about:
- How this relates to the standard library's iostream system. It seems pretty redundant.
- Why "println" exists.
[0] https://en.cppreference.com/w/cpp/io/print