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::function
object typically uses more memory than theauto
-declared object1.- invoking a closure via a
std::function
object 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 auto
ed 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
auto
variables, 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::function
template, and that has a fixed size for any given signature. When this size is not adequate for the closure,std::function
constructor will allocate heap memory to store the closure - leading to typical result thatstd::function
object 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. ↩︎