printf("Number %d, String %s", n, s);
but the types of n and s aren't int and char*, all hell breaks loose and you have the origin of a million CVEs. But how do you make that function signature typesafe, without the tools of modern templates? You sort of can't. One thing you can do is do string append stuff, but the syntax then is annoying: print("Number " + to_string(n) + ", String " + s);
This also creates a bunch of temporary strings, which is not ideal (and still relies on operator overloading, btw). In this world, using operators for this does make some sense: std::cout << "Number: " << n << ", String " << s;
Like, seen from this perspective, it's not the worst idea in the world, it does work nicely. The syntax really isn't too bad, IMHO (the statefulness part, though, really sucks). It also allows you to add formatting for your own types: just overload operator<<!Clearly, properly type-safe std::format is vastly superior, but the C++ of the 90s simply didn't have the template machinery to make that part of the standard library.