4

For example, the loop:

std::vector<int> vec;
...
for (auto& c : vec) { ... }

will iterate over vec and copy each element by reference.

Would there ever be a reason to do this?

for (int& c : vec) { ... }
user473973
  • 681
  • 2
  • 12
  • 23
  • 2
    Coding guidelines from the place you work at. – JustSid Jan 14 '14 at 14:13
  • by using auto& you avoid copy constructing each element – Exceptyon Jan 14 '14 at 14:14
  • Think about what would happen if you have a vector of something else than plain `int`, like some big structure or something that takes considerable resources to copy. And yes, without the reference, those objects will be copied. – Some programmer dude Jan 14 '14 at 14:14
  • 3
    You should indeed obtain a reference instead of copying the elements, but the OP is not asking about that. – A Person Jan 14 '14 at 14:16
  • @JoachimPileborg Thanks. What about using `decltype(vec)` instead? – user473973 Jan 14 '14 at 14:16
  • decltype(vec) gives you `std::vector`. – A Person Jan 14 '14 at 14:16
  • @APerson oh yeah, you're correct. – user473973 Jan 14 '14 at 14:17
  • @user473973 `auto` was invented to avoid going through decltypes and fishing out the element type, when the compiler can do it just fine. `auto` is resolved at compile time, so you are not sacrificing the static typing by using it. – Sergey Kalinichenko Jan 14 '14 at 14:18
  • I recommend reading [GotW #92](http://herbsutter.com/2013/06/07/gotw-92-solution-auto-variables-part-1/), [GotW #93](http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/) and [GotW #94](http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/), which answer your question and much more re. `auto`. – Sander De Dycker Jan 14 '14 at 14:25
  • 1
    @JoachimPileborg `for (int& c : vec)` will not compile for vectors of a different type than `int`. – fredoverflow Jan 14 '14 at 14:49

5 Answers5

5

The two code snippets will result in the same code being generated: with auto, the compiler will figure out that the underlying type is int, and do exactly the same thing.

However, the option with auto is more "future-proof": if at some later date you decide that int should be replaced with, say, uint8_t to save space, you wouldn't need to go through your code looking for references to the underlying type that may need to be changed, because the compiler will do it for you automatically.

Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
  • 1
    Well it's kind of aesthetic personal preference only, same "future-proofness" in such case could be achieved with a `typedef`. – luk32 Jan 14 '14 at 14:20
  • @luk32 Although this is true in case of the containers from the Standard C++ library, it may not be true for your own templates that expose `begin()` and `end()`, but lack the appropriate `typedef`s. One can still do it with a sequence of `decltype`s, but that wouldn't be nearly as pretty as a simple `auto`. – Sergey Kalinichenko Jan 14 '14 at 14:26
  • I would assume that custom templates would be prepared in a good manner. I just wanted to point out it's basically just aesthetical / user friendliness difference, not functional. Thus, there is essentially no difference which notation or syntax is used. I agree auto is way more simpler and better looking in some cases. But some people really like the strong typing aspect of c++, with `auto` it's lost a bit IMO. Personally I don't care so much, but some do. – luk32 Jan 14 '14 at 14:37
  • @luk32: If the alternatives are `auto&` or `container::reference_type`, I'd choose the former any day (shorter, just the same information). The interesting argument is choosing between a precise type `int&` and a less precise `auto&`, where the former conveys more information than the latter. Is that additional information *worth* having to look the type rather than just defaulting to `auto` everywhere? That is a matter of taste. For me it is (at least for code I intend on maintaining down the road) – David Rodríguez - dribeas Jan 14 '14 at 14:40
5

Use auto wherever it makes the code better, but nowhere else. Understand the impact using auto overly-liberally has on maintainability.

The question here really is if there is any reason why you shouldn't use auto for anything you can.

Well, let's get one thing out of the way first of all. The fundamental reason why auto was introduced in the first place was two-fold.

First, it makes declarations of complex-type variables simpler and easier to read and understand. This is especially true when declaring an iterator in a for loop. Consider the C++03 psudocode:

for (std::vector <Foo>::const_iterator it = myFoos.begin(); it != myFoos.end(); ++it)

This can become much more complex as the complexity of myFoos's type becomes more complex. Moreover if the type of myFoos is changed in a subtle way, but in a way that's insignifigant to the loop just written, the complex declaration must be revisited. This is a maintennance problem made simpler in C++11:

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

Second, there are situations which arise that are impossible to deal with without the facilities provided by auto and it's sibling, decltype. This comes up in templates. Consider (source):

template<typename T, typename S>
void foo(T lhs, S rhs) {
  auto prod = lhs * rhs;
  //...
}

In C++03 the type of prod cannot always be inferred if the types of lhs and rhs are not the same. In C++11 this is possible using auto. Alas it is also possible using decltype, but this was also added to C++11 along with auto.


Many of the C++ elite suggest that you should use auto anywhere possible. The reason for this was stated by Herb Sutter in a recent conference:

It's shorter. It's more convinient. It's more future-proof, because if you change your function's return type, auto just works.

However they also acknowledge the "sharp edges." There are many cases where auto doesn't do what you want or expect. These sharp edges can cut you when you want a type conversion.


So on the one hand we have a highly respected camp telling us "use auto everywhere possible, but nowhere else." This doesn't feel right to me however. Most of the benefits that auto provide are provided at what I'm going to call "first-write time." The time when you are first writing a piece of code. As you're writing a big chink of brand-new code you can go ahead and use auto virtually everywhere and get exactly the behavior you expect. As you're writing the code you know exactly what's going on with your types and variables. I don't know about you, but as I write code there is a constant stream of thoughts going through my head. How do I want to create this loop? What kind of thing do I want that function to return so I can use it here? How can I write this so that is fast, correct, and easy to understand 6 months from now? Most of this is never expressed directly in the code that I write, except that the code that I write is a direct result of these thoughts.

At that time, using auto would make writing this code simpler and easier. I don't have to burden my mind with all the little minute of signed versus unsigned, reference vs value, 32 bit vs 64 bit, etc. I just write auto and everything works.

But here's my problem with auto. 6 months later when revisiting this code to add some major new functionality, that buzz of thought that was going through my mind as I first write the code has long been extinguished. My buffers were flushed long ago, and none of those thought are with me anymore. If I have done my job well, then the essence of those thoughts are expressed directly in the code I wrote. I can reverse-engineer what I was thinking by just looking at the structure of my functions and data types.

If auto is sprinkled everywhere, a big part of that cognizance is lost. I don't know what I was thinking would happen with this datatype because now it's inferred. If there was a subtle relationship taking place with an operator, that relationship is no longer expressed by the datatypes -- it's all just auto.

Maintenance becomes more difficult because no I have to re-create much of that thought. Subtle relationships become more clouded, and everything is just harder to understand.

So I'm not a fan of using auto everywhere possible. I think that makes maintenance harder than it has to be. That's not to say I believe that auto should only be used where it's required. Rather, it's a balancing act. The four criteria I use to judge the quality of my (or anyone's code) are: efficiency, correctness, robustness, and maintainability. In no particular order. If we design a spectrum of auto use where one side is "purely optional" and the other side is "strictly required", I feel that in general the closer to "purely optional" we get, the more maintainability suffers.

All this to say, finally, that my philosophy can be nutshelled as:

Use auto wherever it makes the code better, but nowhere else. Understand the impact using auto overly-liberally has on maintainability.

John Dibling
  • 94,084
  • 27
  • 171
  • 303
2

That depends on what you want to do with c:

  • You want to work with copies? Use auto c
  • You want to work with original items and possibly modify them? Use auto& c
  • You want to work with original items and not modify them? Use const auto& c
JuanR
  • 4,441
  • 15
  • 26
  • Don't forget the universal reference `auto&& c`: http://stackoverflow.com/questions/13130708/what-is-the-advantage-of-using-universal-references-in-range-based-for-loops – rubenvb Jan 14 '14 at 14:16
  • @rubenvb: I don't think it makes sense to use `auto&&` inside a range-based for loop, and I particularly would find it more *obscure* than the straight uses of `auto` in the answer (or the original type) – David Rodríguez - dribeas Jan 14 '14 at 14:42
2

There are two conflicting insterests here. On the one side, it is just simpler to type auto& everywhere than figuring the exact type en each loop. Some people will claim that it is also more future-proof if the type stored in the container changes in the future --I don't particularly agree, I'd rather have the compiler complain* and let me figure out if the assumptions made in the loop about the type still hold.

On the other side, by using auto& (instead of the more explicit int&) you are hiding the type from the reader. The maintainer reading the code will have to think what auto means in this particular context, while int clearly has a single meaning. The same people, or at least a subset of them, will claim that you don't need to think, that the IDE will tell you the type if you hover the mouse over the variable... but I don't use an IDE nor particularly like the mouse...

Over all, this is mainly a matter of style. I prefer to see the types in the code rather than have the compiler infer them for me, as I find it easier to understand when I go back to the code some time later. But for quick coding when I don't envision having to maintain this code a week from now auto is more than sufficient.

* This assumes that you use a standard compliant compiler of which VisualStudio is not an example. The assumption is that if the type is wrong, a non-const reference won't bind to the value returned by dereferencing the iterator. In VS, the compiler will gladly convert types and bind a non-const reference to the rvalue! Maybe this is why Sutter, comming from the VS world suggests using auto everywhere!

David Rodríguez - dribeas
  • 192,922
  • 20
  • 275
  • 473
0

I agree with dasblinkenlight's answer, but since you are asking when int& is better than auto&, I can paraphrase it this way:

Use int& when you would like your code to break if/when someone decides to change the type of your vector.

For example: your vectors usually contain int16_t, but this particular one requires greater precision (assuming int has 32-bit or greater precision). You don't want someone to change the type from int to int16_t and get a program that contains a silent overflow in calculations.

Another example: your code looks like this:

namespace joes_lib
{
    int frobnicate(int);
}

for (int& c : vec) { c = frobnicate(c); }

Here, if someone changes vec to something like vector<int16_t> or, worse, vector<unsigned>, auto will silently succeed and lead to loss of precision in joe's library function. Compiler may or may not generate warnings about this.

These examples look clumsy and obscure, so you may want to comment such usage of non-auto types in loops.

Community
  • 1
  • 1
anatolyg
  • 23,079
  • 7
  • 51
  • 113