There are several reasons why I dislike auto for general use:
- You can refactor code without modifying it. Yes, this is one of the things often listed as a benefit of using auto. Just change the return type of a function, and if all of the code that calls it uses auto, no additional effort is required! You hit compile, it builds - 0 warnings, 0 errors - and you just go ahead and check your code in without having to deal with the mess of looking through and potentially modifying the 80 places the function is used.
But wait, is that really a good idea? What if the type mattered in a half dozen of those use-cases, and now that code actually behaves differently? This also can implicitly break encapsulation, by modifying not just the input values, but the behavior itself of the private implementation of other classes that call the function.
1a. I'm a believer in the concept of "self-documenting code". The reasoning behind self-documenting code is that comments tend to become out-of-date, no longer reflecting what the code is doing, whereas the code itself - if written in an explicit manner - is self-explanatory, always stays up to date on its intent, and won't leave you confused with stale comments. If types can be changed without needing to modify the code itself though, then the code/variables themselves can become stale. For example:
auto bThreadOK = CheckThreadHealth();
Except the problem is that CheckThreadHealth() at some point was refactored to return an enum value indicating the error status, if any, instead of a bool. But the person who made that change missed inspecting this particular line of code, and the compiler was of no help because it compiled without warnings or errors.
- You may never know what the actual types are. This is also often listed as a primary "benefit" of auto. Why learn what a function is giving you, when you can just say, "Who cares? It compiles!"
It even kind of works, probably. I say kind of works, because even though you're making a copy of a 500 byte struct for every loop iteration, so you can inspect a single value on it, the code is still completely functional. So even your unit tests don't help you realize that bad code is hiding behind that simple and innocent-looking auto. Most other people scanning through the file won't notice it on first glance either.
This also can be made worse if you don't know what the type is, but you choose a variable name that makes a wrong assumption about what it is, in effect achieving the same result as in 1a, but from the very beginning rather than post-refactor.
- Typing the code when initially writing it isn't the most time consuming part of programming. Yes, auto makes writing some code faster initially. As a disclaimer, I do type > 100 WPM, so maybe it doesn't bother me as much as others. But if all I had to do was write new code all day, I'd be a happy camper. The most time consuming part of programming is diagnosing hard-to-reproduce, edge-case bugs in the code, often which result from subtle non-obvious problems - such as the kind overuse of auto is likely to introduce (reference vs. copy, signed vs. unsigned, float vs. int, bool vs. pointer, etc.).
It seems obvious to me that auto was introduced primarily as a workaround for terrible syntax with standard library template types. Rather than try to fix the template syntax that people are already familiar with - which may also be nearly impossible to do because of all of the existing code it could break - add in a keyword that basically hides the problem. Essentially what you might call a "hack".
I actually don't have any disagreement with the use of auto with standard library containers. It's obviously what the keyword was created for, and functions in the standard library are not likely to fundamentally change in purpose (or type for that matter), making auto relatively safe to use. But I would be very cautious about using it with your own code and interfaces that may be much more volatile, and potentially subject to more fundamental changes.
Another useful application of auto that enhances the capability of the language is creating temporaries in type-agnostic macros. This is something you couldn't really do before, but you can do it now.