Honestly I can't think of many notable abuses of operator overloading in C++.
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.
I remember a university project where I used the >> (and <<) operators to "send" data between services.
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 did something similar as a student, making my own exception class with std::ostream:
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.
There could be a couple of examples in Boost like Boost Spirit [0]. Qt had some like putting stuff in containers with bitshift operator. There was a GUI toolkit that used + operator to put widgets on a window.
I've seen operator* overloaded to return RAII guards, including ones for RCU, and operator() overloaded for so many things that should just be lambdas it's hilarious
It was certainly how I learned to do it, but lambdas have been around for over a decade and I still see people writing functors. The only use case I've seen where it made sense is for things like coroutines.
For std::visit() (std::variant.visit in c++26 I see) which is the visitor pattern, you can use a functor with multiple visit types or roll your own overload() template to merge multiple lambdas into one class.
But why would I want to do this instead of just having multiple lambdas that capture the same values by reference, or using shared_ptr and synchronization? I can see lifetimes of the data being an issue, but you shouldn't be calling std::visit in a way where that could cause a problem without synchronization anyway.
Lambdas these days cover 99% of the needs for custom function objects. But for that 1% it is useful to be able to have full control of your closure.
For 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.
That example seems contrived. Why would I want to know something was called n-times? Even for benchmarking I would just capture an integer and increment it.
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.
I actually ran into that at work a few days ago. I wanted to provide a callback that accumulated stuff, and check that the total was equal to what was expected, or crash the program otherwise (fail fast). I could have equivalently written the output of the test to a capture-by-ref variable and checked it outside if I really wanted to.
MFC also has CComPtrBase which uses & to represent pointer lifetimes to COM objects such as while(pEnum->Next(1, &pFilter, &cFetched) == S_OK). Especially fun when debugging DirectShow filtergraphs someone made in the UI completely. There is more of an explanation here: https://devblogs.microsoft.com/oldnewthing/20221010-00/?p=10...
> If you object to iostream on religious or stylistic grounds,
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.
Streams have numerous design problems (e.g. representation is managed by the stream, not the thing you’re printing) making the shift operator syntax the least of the problems.
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.