C++ 11 Auto: How to use and avoid abuse
acodersjourney.com
acodersjourney.com
I don't actually think the first example was that bad, modulo some context. Sometimes even knowing the type for this kind of thing isn't super important to understand what the code is doing; ala opaque types.
The original function name was named poorly however.
I always want my variable names to be as long and descriptive as possible. We're no longer in the teletype era and our monitors are huge. With autocomplete there is no reason to keep variable names short
> RandomizeArrayPositionUsingFisherYatesShuffle
Since the algorithm detail is something I don't want to think about when I use it and something that the implementer should be able to change without changing the contract.
Apple publishes some very good guidelines for Objective-C and Swift, the latter here: https://swift.org/documentation/api-design-guidelines/
Generally, I'd name this method according to the role of the result, and not it's type. While "xxInteger()" isn't good, "numberOfXX()" returning an integer type does improve readability and would still be understandable assigned into an auto.
Remember, you're not writing code for you, you're writing code for future you. Or worse, for the next guy who comes into the codebase. They should be able to derive your intent without having to figure out what each method signature is, and reading all the API docs due to lack of code readability.
The problem here is actually is an old one of failing to separate a getter from a command.
It looks like ConjureMagic is causing side effects and modifying the state of whatever class it belongs to.
This is also the reason one can't answer the question "what the heck is a?". If the "ConjureMagic" is only a getter and does not modify class state, it may probably need a better name, like "GetMaxMagicPossible". That renders an auto no longer confusing, because this name explains more than the return type. If, on the other hand this is just a command to conjure the magic and we are getting the remaining amount only to immediately pass it to the member "SetMagic" function, then there was no point in returning this value - it could be done inside the "ConjureMagic" itself.
Member variables are easy to reason about; they behave exactly like the type they are, and in the case of built in types, have extremely consistent behavior. Getters / setters are a black box of mystery; I like to reserve that pattern for when there's going to be extra work to retrieve some value, so my call site knows it needs to tip-toe around possible failures. While I can see the value in using getters/setters for everything in something like an API or framework, I don't see the value in trying to use them all the time, "just in case."
In Herb Sutter's words, avoiding auto since it makes the code "unreadable":
...reflects a bias to code against implementations, not interfaces. Overcommitting to explicit types makes code less generic and more interdependent, and therefore more brittle and limited. It runs counter to the excellent reasons to “write code against interfaces, not implementations”
See http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-...
The second issue (async) is quite similar in my opinion, and in practice your async handler will be explicitly decoupled and strongly typed at some point for reasons of testability so the example is describing also an OO design issue and not an issue with auto.
There may be real problems with auto but this article did not provide any compelling evidence of them.
I believe Stroustrup is correct that we're all beginners at some point, but we quickly move up. It makes more sense to optimize for "proficient, but not expert" than to optimize for "beginner," because more people are at the proficient level, and they stay there longer.
I agree that code shouldn't be unnecessarily complex. But I would quit any project that constantly brought up "but what if a programmer were brand new when they looked at this code?" in reviews. Then again, if other programmers have the same reaction, perhaps the project really would be full of nothing but beginners.
auto ptr = make_shared<T>()
Reads very nicely so I agree that if the type should be somewhere in the auto expression. That is, use auto to remove redundancy. I just wish they would have extended type deduction to lambda arguments so I could do: [](a,b){ ... }
Instead of: [](int a, int b) {...}
My understanding is that they're allowing: [](auto a, auto b) {...}
Bleh.Is valid if a and b are types.
Explicit casting if often required to make sure subtle bugs won't show up in production.
1. The IDE cannot deduce the type within templates, and more code is moving to templates (e.g. generic lambdas in C++14)
2. Code is often read outside of IDEs: code review, search engines, etc.
3. Should C++ become a language that requires an IDE to work effectively, like Java? Probably not.
Really not reviewing a program inside an IDE is a poor idea in general.
VS, Netbeans, Eclipse, Android Studio, SQL Developer.
In general, I don't think C++ (or D) devs should be using deduced return types in the public interface, if at all possible.
vs
auto it = hashmap.begin();
I find auto useful for cutting down some of the verbosity of templates STL containers, but I can see how over use can lead to code requiring much more referencing if maintaining code that rarely defines types.
No doubt auto is extremely useful in a case like the one you mentioned. In fact, even more so in here:
for (std::unordered_map<std::string, std::unordered_map<std::string, long>*>::const_iterator it = a.begin(); ...)
So my hashmap handling looks like this:
MHashMap userlist = new MHashMap<HUser>();
HUser *user = userlist->First();
while(user) { user->DoSomething(); user = userlist->Next(); }
A lot of times, the type is unimportant but when it is, this kind of usage can bloat a simple task into 5 to 15 minutes of hunting through headers or grepping.
It's strange to think of an IDE feature so impacting a language feature. The stack is not supposed to affect in that direction but this is one case where it really does.
Of the 3 options below, the third seems best to me:
SomeReallyLongTypeName* x = foo(); // old-school
auto x = foo(); // c++11
pointer x = foo(); // c++17 concepts smart_ptr<auto> x = foo();