I feel that this is something that would be fixed by writing your test suites first. If you think you have designed a tremendously clever API, but then the code that calls it is an absolute mess, then you have not designed a tremendously clever API.
General principles of restraint and not having to use every available language feature all the time apply here as everywhere - keep it simple, stupid. The only WTF I want when reading your code is, “I didn’t realise there could be such a straightforward way to do it”.
I had designed a system with what I thought was a good API, wrote the test for it and I was satisfied how simple it was to use the thing but then, when it came to integrate it, it was clunky and no good way to actually use the system.
I don’t think it’s always obvious when an API is good or bad, even with tests.
deleted by creator
I’ve been annoyed at this in Python sometimes, since its containers have some operations named only, some operator only, and some available as both.
C++'s standard containers mostly have useless operator overloads though, so its technical win here isn’t particularly impressive. One thing you definitely notice when porting C++ code to other languages is just how many silly dances various APIs force you to use for common tasks.
deleted by creator
I agree we should use operator overloading only when it really fits the use case. Especially the function call operator is easily misused.
I don’t completely agree with Raymond Chen here though. Firstly I don’t think providing both an explicit Load() function and the function call operator is the solution. Just keep things simple and obvious: provide Load() and remove the function call operator.
Secondly, why is StorageLoader even a class (or actually a struct here, but we know that’s the same thing in C++)? Unless Raymond is leaving out something essential, there is no state. Just make a function:
template<typename DataType> LoadFromStorage(StorageOptions<DataType> const* options) { // ... }
This solves all the problems: you can simply call it, even without operator overloading (because it’s already a function), and doesn’t make it awkward to specify the data type we need. We’re writing C++ here, not Java where everything has to be a class even if it doesn’t have any reason to be.