21

What are the pros/cons to using the auto keyword, especially in for loops?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Seems like if you don't know whether you have an iterator for a map or a vector you wouldn't know whether to use first or second or just directly access properties of the object, no?

This reminds me of the C# debate on whether to use the keyword var. The impression I'm getting so far is that in the C++ world people are ready to adopt the auto keyword with less of a fight than var in the C# world. For me my first instinct is that I like to know the type of the variable so I can know what operations I can expect to perform on it.

5
  • 3
    Wait! There was a fight about whether to use var? I missed that.
    – pdr
    Commented May 12, 2012 at 5:19
  • 22
    Even better, you can just use for (auto& it : x) (or without reference if you want copying) Commented May 12, 2012 at 7:46
  • 8
    Seems to me if you're writing a loop to iterate over the contents of x and you don't even know what x is, you shouldn't write that loop in the first place ;-)
    – nikie
    Commented May 13, 2012 at 9:10
  • @fish: range based for-loops rules, but I would have been pedantic and done: 'for (T& it : x)' instead when using range based for-loops, as I feel using auto there is less informative. Kind of a misuse of auto in my mind.
    – martiert
    Commented Jan 3, 2013 at 13:41
  • The fight over using var was a little silly, especially in retrospect. See Cargo cult programming. Commented Jan 25, 2015 at 20:28

7 Answers 7

41

The motivations in C++ are more extreme, as types can become vastly more convoluted and complex than C# types due to metaprogramming and other things. auto is faster to write and read and more flexible/maintainable than an explicit type. I mean, do you want to start typing

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

That's not even the full type. I missed off a couple template arguments.

5
  • 9
    +1 for the example, but that also tells something about the state of "modern" C++.
    – zvrba
    Commented May 12, 2012 at 13:37
  • 24
    @zvrba: Yeah- that the generic facilities are much more powerful than C#'s.
    – DeadMG
    Commented May 12, 2012 at 13:47
  • 5
    that's what typedef is for
    – gbjbaanb
    Commented May 13, 2012 at 11:51
  • 20
    @gbjbaanb No, that’s what auto is for. By design. typedef helps, but auto helps more. Commented May 13, 2012 at 13:13
  • 1
    typedef invalidates the argument that "not using auto makes for really long types"
    – Michael
    Commented Mar 19, 2018 at 20:17
28

In your example:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

there has to be a declaration for x visible. Therefore the type of it should be obvious. If the type of x isn't obvious, then the method is too long, or the class is too large.

3
  • 9
    Also, x is a very bad variable name for a container. In some situations you can most likely just look at the (semantically valuable) name and infer the possible operations.
    – Max
    Commented May 11, 2012 at 23:39
  • @Max: only used x as an generic example, I tend to use quite descriptive variable names.
    – User
    Commented May 12, 2012 at 0:05
  • @User Of course, I didn't assume that was a real-world example ;)
    – Max
    Commented May 12, 2012 at 0:28
14

Objection! Loaded question.

Can you explain to me why the third code has ?? in it, yet the first and second don’t? For fairness’ sake, your code must read as follows:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

There. Same problem even though you didn’t use auto.

And in all cases, the answer is the same: Context matters. You can’t meaningfully talk about a piece of code in isolation. Even if you hadn’t used templates but some concrete type, this would only have moved the problem somewhere else, since the reader of your code would have to know about the declaration of said type.

If use of auto in those situations render your code unreadable you should treat this as a warning sign that there’s something wrong with your code design. Of course, there are cases where low-level details matter (such as when dealing with bit operations or legacy API) in which cases an explicit type might aid readability. But in general – no.

Regarding var (since you explicitly mentioned it), there’s also a vast consensus in the C# community for using var. Arguments against its use are generally built on fallacies.

10
  • 1
    I think the point was, with auto you dont know what that you put next... is it your code specific "something" or is it a data type related unpacking to get to your data object that has the method "something" Commented Oct 2, 2015 at 8:28
  • 2
    @Ptolemy And my point is: in the other two codes you also don’t know (generally) what to put next: T is as opaque to the user as auto. Yet one is supposed to be fine and the other not?! That doesn’t make sense. In the case of OP, T is a stand-in for an arbitrary type. In real code, it may be the use of templates (for typename std::vector<T>::iterator…) or a class interface. In both cases, the actual type is hidden from the user, and yet we routinely write such code without issues. Commented Oct 2, 2015 at 11:44
  • 1
    Actually, yes you do. If its a vector, you know that you need to do -> and then you have access to your data type. If its a map, you know you need to do ->second-> and then you have access to your data type, if its auto, you don't know what you need to do to get access to your data type. You seem to be confusing the "what is the data type contained in the STL collection" with "which STL collection type do we have". auto makes this problem worse. Commented Oct 2, 2015 at 11:50
  • 1
    @Ptolemy All these arguments are just as true when using auto. It’s trivial to see what operations x supports from context. In fact, the type gives you no additional information: in either case you need some secondary (IDE , documentation, knowledge/memory) to tell you the set of supported operations. Commented Oct 2, 2015 at 11:52
  • 1
    @Ptolemy That is only true if you are in the highly convoluted situation that you don’t know what begin returns but you do know what std::vector<>::iterator is. And you need to use a bad programming tool that cannot give you this information trivially. This is highly convoluted. In reality, you either know both begin and iterator or neither, and you should be using an IDE or editor that can readily make the relevant information available to you. Every modern IDE and programming editor can do that. Commented Oct 2, 2015 at 11:59
11

PRO

Your code :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

is not going to compile, because of the template dependent name.

This is the correct syntax :

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Now look how long the type declaration is. This tells why the auto keyword was introduced. This :

for( auto it = x.begin(); it != x.end(); i++)

is more concise. So, this is a pro.


CON

You have to be a bit careful. With the keyword auto, you get the type you declared.

For example :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

vs

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

To conclude : yes, you should it, but not overuse it. Some people tend to use it too much, and put auto everywhere, like in next example :

auto i = 0;

vs

int i = 0;
1
  • auto i = 0. Guilty. I do that. But that's because I know that 0 is a literal of type int. (and an octal constant ;-) ) Commented Feb 15, 2017 at 20:59
7

YES, you should! auto does not erase the type; even if you "don't know" what x.begin() is, the compiler knows and will report an error if you try to use the type wrongly. Also, it is not unusual to emulate map with a vector<pair<Key,Value>>, so the code using auto will work for both dictionary representations.

5

Yes, you should use auto as a default rule. It has raw advantages over explicitly specifying the type:

  • It does not make you type things the compiler is already aware of.
  • It makes the type of variables "follow along" any change in return value types.
  • Doing so, it prevents silent introduction of implicit conversions and slicing in local variable initialization.
  • It removes the need of some explicit type calculations in templates.
  • It removes the need of naming return types with long names. (the ones you copy-paste from compiler diagnostics)

This is where you have a choice. There are also cases where you don't have a choice:

  • It permits declaration of variables of unutterable types, like the type of a lambda.

Provided that you know exactly what auto does, it has no disadvantages.

1

You should use auto when you don’t care what exactly the type is. As an example, x.begin() will give me an iterator with exactly the right type, and auto it = x.begin() will create a variable with the exactly right type. I don’t care what the type is.

Or auto x = f(a); auto y = f(b); Now i know that x, y have the correct type. Not bigger or smaller than needed. Do I care what exactly the type is? If you don’t, use auto.

Not the answer you're looking for? Browse other questions tagged or ask your own question.