Cleverness is a register that is easy to overflow, and too many don't have to good taste to avoid doing so.
Give me names every time.
Doing linear algebra without any operator overloading is just unreadable
symbols and names are merely suggestive mnemonic devices. They can’t ever specify actual behaviors.
Accept it and supplement with English. That’s what math does. You have to read the context.
> optimized to tersness
That is a form of readability. If you think there is a conflict you need to explain mathematicians wouldn’t care about readability.
The worst is probably the original idea to overload the bit shift operators for stream I/O, something which never really caught on outside the standard library.
[0] https://www.boost.org/doc/libs/1_78_0/libs/spirit/doc/html/s...
In the case of ostream and operator<<, this pattern reduces the number of intermediate objects that would otherwise be constructed.
If you object to iostream on religious or stylistic grounds, there's always fmt which is more like Go or Python string interpolation.[0]
No I object to it on usability grounds. It took more than 30 years for the committee to admit it but C++ now has typesafe std::print/std::format finally, after admonishing programmers for using `printf` in C.
Iostreams have a lot of issues, but overloading<< is a very minor one.
The code was a simulation of a parallel system with multiple services that sent messages between them, or something like that. Instead of using serviceA.send("hello",serviceB) you had something like serviceA >> "hello" >> serviceB.
I really loved that (my classmates not so much)
throw exceptionC() << "error code: " << t;
I often found myself having to format error strings for exceptions, so I thought I could just do it like cout in one line. I know now this is bad for i18n strings.
throw exceptionC("error code: ", t);
?Ever since perfect forwarding I've been using this pattern to get out of arrow hell:
template <typename Arg, typename...Args>
string to_string(Arg &&arg, Args &&...args) {
stringstream buf;
buf << arg;
((buf << std::forward<Args>(args)), ...);
return buf.str();
} SinOsc b => Gain g => BiQuad f => dac;
https://chuck.stanford.edu/doc/language/oper.html#chuckFor example how would you implement std::function without overloaded operator()?
Also lambdas are defined in term of structs with overloaded operator ().
Without overloading, the standard could still ad-hoc define the specifications of lambdas and std::function, std:: ref, etc, but the language would be worse off.
To be mildly pedantic, printing in a destructor is horrible practice because stdout/stderr might be pipes or sockets and writing might fail. It's a really bad idea to do anything in a destructor that effects anything but the class being destroyed for those reasons, and when you get an error, it shows up as an opaque exception or trace or hang at the end of a scope instead of where it actually mattered.
MFC extends this idea to network programming, allowing the use of shift operators to send and receive data.
I'm on the permissive side (Haskell and Lenseful Haskell at that!), but make it consistent and principled and it's usually fine.