70

In C++11, I can iterate over some container like so:

for(auto i : vec){
   std::cout << i << std::endl;
}

But I know that this needlessly - needlessly, since I only need to print the values of vec - makes a copy of (EDIT: each element of) vec, so instead I could do:

for(auto &i : vec){
   std::cout << i << std::endl;
}

But I want to make sure that the values of vec are never modified and abide by const-correctness, so I can do:

for(const auto &i : vec){
   std::cout << i << std::endl;
}

So my question is: If I only need to look at the values of some container, wouldn't the very last loop (const auto &i) always be preferred due to the increased effieciency of not having an extra copy of (EDIT: each element of) vec?

I have a program that I'm developing in which I'm considering making this change throughout, since efficiency is critical in it (the reason I'm using C++ in the fist place).

user2052561
  • 1,339
  • 2
  • 15
  • 18

3 Answers3

94

Yes. The same reason if you only ever read an argument you make the parameter const&.

T        // I'm copying this
T&       // I'm modifying this
const T& // I'm reading this

Those are your "defaults". When T is a fundamental type (built-in), though, you generally just revert to const T (no reference) for reading, because a copy is cheaper than aliasing.


I have a program that I'm developing in which I'm considering making this change throughout, since efficiency is critical in it

  1. Don't make blind sweeping changes. A working program is better than a fast but broken program.
  2. How you iterate through your loops probably won't make much of a difference; you're looping for a reason, aren't you? The body of your loop will much more likely be the culprit.
  3. If efficiency is critical, you want to use a profiler to find which parts of your program are actually slow, rather than guess at parts that might be slow. See #2 for why your guess may be wrong.
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • "copy is cheaper than aliasing" only with most fundamental types, besides move semantics can't been used in the OP's situation and since it T can be any type, a reference would be the right choice. – Tim Jun 10 '13 at 20:40
  • 1
    "*"copy is cheaper than aliasing" only with most fundamental types*" Which is why the first half of that sentence is dedicated to saying it applies to fundamental types...? Obviously if you don't know the type, you use the general case. – GManNickG Jun 10 '13 at 20:41
  • @Tim: No worries, it's good to mention the general case when the type may not be readily known. – GManNickG Jun 10 '13 at 20:49
  • 1
    Why would the compiler generate different code for for example "int" and "const int &" in a range-loop ? It makes a difference when passing args to a function, but in a range-loop the compiler has everything it needs to optimize the hell out of it. I tried disassembling a few range-loops and the generated code was the same. Same for structs with trivial dtor and trivial copy-ctor. – Sergio Martins Dec 21 '15 at 22:55
  • 2
    @SergioMartins: It wouldn't, that why you should just use `const T&`. In general this what you want, and it's pointless effort to further modify it to "`const T`" or just "`T`" when you happen to know `T`; the compiler will do this right for you. – GManNickG Dec 22 '15 at 20:41
  • 3
    with `const auto&` you signal your intention to not modify anything and that you won't need a copy either. Even if the performance was the same I would go with that because it makes your code more expressive. – hochl Feb 03 '17 at 08:00
7

Disclaimer: In general the difference between auto and auto& is subtle, partly a matter of style, but sometimes also a matter of correctness. I am not going to cover the general case here!

In a range based for loop, the difference between

for (auto element : container) {}

and

for (auto& element_ref : container) {}

is that element is a copy of the elements in the container, while element_ref is a reference to the elements in the container.

To see the difference in action, consider this example:

#include <iostream>

int main(void) {
    int a[5] = { 23,443,16,49,66 };

    for (auto i : a) i = 5;       
    for (const auto& i : a) std::cout << i << std::endl;
    for (auto& i : a) i = 5;   
    for (const auto& i : a) std::cout << i << std::endl;    
}

It will print

23
443
16
49
66
5
5
5
5
5

because the first loop works on copies of the array elements, while the second actually modifies the elements in the array.

If you dont want to modify the elements then often a const auto& is more appropriate, because it avoids copying the elements (which can be expensive).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
2

Imagine if your vector contains strings. Long strings. 5000 long strings. Copy them unnecessarily and you end up with a nicely written for loop that is awfully inefficient.

Make sure your code follows your intention. If you do not need a copy inside of the loop, do not make one.

Use a reference & as suggested above, or iterators.

Visiedo
  • 377
  • 5
  • 9