Despite some pitfalls described in EMCpp item 2 and 6, auto variables are preferred for they must be initialized, are generally immune to type mismatches that can lead to portability or efficiency problems, can ease the process of refactoring, and typically require less typing than variables with explicitly specified types.
Avoidance of Uninitialized variables
auto variavbles have their type deduced from their initializer, so they must be initialized.
|
|
Avoidance of Syntactic Verbosity
In order to express the type of the value pointed to by an iterator, without auto, we may write like this:
|
|
Thanks to auto, now we can declare a local variable whose value is that of a dereferenced iterator with ease:
|
|
Ability to Hold Closure
Because auto uses type deduction, it can represent types known only to compilers, such as lambda expressions:
|
|
or in C++11, a little more verbose:
|
|
Without using auto, since lambda expressions yield closures, which are callable objects, we can store them in std::function objects:
|
|
As we can see, syntactic verbosity makes auto a preferred choice. Besides that, there are two more reasons to choose auto:
std::functionobject typically uses more memory than theauto-declared object1.- invoking a closure via a
std::functionobject is almost certain to be slower than calling it via anauto-declared object2.
In summary, auto wins the competition between auto and std::function for holding a closure.
Avoidance of Unexpected Implicit Conversions
Consider this code:
|
|
This looks perfectly reasonable, but there’s a problem: the key part of a std::unorderd_map is const, so the type of std::pair in the hash table isn’t std::pair<std::string, int>, but std::pair<const std::string, int>. Since the constness hasn’t been declared for the variable p in the loop, compilers will create a temporary object of the type that p wants to bind to by coping each object in m, then binding the reference p to that temporary object, and finally destroy the temporary at the end of each loop iteration. This is almost certain to be an unwanted behavior - we probably intend to simply bind the reference p to each element in m directly.
Such unintentional type mismatches can be autoed away:
|
|
Avoidance of Explicit Type Revising during Refactoring
auto types automatically change if the type of their initializing expression changes, and that means that some refactoring (e.g., change the return type of a function from int to long) are facilitated by the use of auto:
- if the results of calling the function are stored in
autovariables, the calling code automatically updates itself the next time we compile - if the results are stored in varibles explicitly declared to be
int, we have to find all the call sites so that we can revise them.
-
An
auto-declared variable holding a closure has the same type as the closure, and uses only as much memory as the closure requires. The type of astd::function-declared variable holding a closure is an instantiation of thestd::functiontemplate, and that has a fixed size for any given signature. When this size is not adequate for the closure,std::functionconstructor will allocate heap memory to store the closure - leading to typical result thatstd::functionobject uses more memory than theauto-declared object. ↩︎ -
This slowness comes from the implementation details of
std::function, which restrict inlining and yield indirect function calls. ↩︎