C++ iterate primitive type container with ranged for

318 Views Asked by At

What would be a better way (performance) to iterate primitive type container with ranged for (for reading elements values) - read elements by ref or by value?

std::vector<int> v;
for (const auto e : v) { std::cout << e; }

or

for (const auto& e : v) { std::cout << e; }

There is: Is it counter-productive to pass primitive types by reference?

Wondering if these 2 things (passing and iterating by ref and value) might be somehow related.

Another note: I do recognize what is the difference between access by ref, const-ref and value as well as copying values - I only interested in what way would perform better for READ-ONLY.

3

There are 3 best solutions below

3
On

For small, primitive types, if you don't want to modify the elements of the container, then it's really a matter of style, although for such 'read-only' access, you're probably safer using the by-value approach (which will actually prevent any unintended modification of the 'originals' - although a const qualifier on the reference will also prevent that).

However, if you do want to modify the contained elements, then you will need to iterate using a reference variable, as shown in the below code sample:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{ 1,2,3,4,5 };

    for (auto e : v) ++e;                   // By value: takes copies
    for (auto f : v) std::cout << f << " "; // Elements unchanged
    std::cout << std::endl;

    for (auto& e : v) ++e;                  // By reference: refers to actual elements
    for (auto f : v) std::cout << f << " "; // Elements incremented
    std::cout << std::endl;

//  for (const auto& e : v) ++e;            // const reference: Compiler error!

    return 0;
}
0
On

I am not saying that this is the only correct approach, but it is a completely valid approach: Do not worry about performance when writing the code. Care about readability and keep performance for when you have something correct and working.

Taking this as premise my advice is the following:

Use this as default when you do not need to modify the containers elements:

for (const auto& e : v) {}

Only when for some reason you need a copy of the elements (but still do not want to modify the elements) use

void foo(int& x) {}
for (auto e : v) {
    e += 5;
    foo(e);
}

If you need to modify the elements use a reference:

 for (auto& e : v) {}

Last but not least, once you have working correct code and you realized that this loop is a bottneck by measuring: Look at the assembly to see what is better.

However, consider that any non-trivial loop body is likely to outweigh the difference between copying a small type and taking a reference.

0
On

In theory, the object variable can potentially be more efficient because it doesn't involve indirection through a reference. However, at least in this simple example the reference version may be optimised to be identical with the object version, so it wouldn't necessarily matter in practice.

Wondering if these 2 things (passing and iterating by ref and value) might be somehow related.

Yes. Same rules of thumb apply to both cases. And in both cases it likely won't matter if everything is inlined by the optimiser.