Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Range based loop: get item by value or reference to const?

Tags:

c++

c++11

People also ask

How does range based for loop work?

Range-based for loop in C++ Range-based for loop in C++ is added since C++ 11. It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.

How does range based for loop work C++?

C++11 introduced the ranged for loop. This for loop is specifically used with collections such as arrays and vectors. Here, the ranged for loop iterates the array num from beginning to end. The int variable var stores the value of the array element in each iteration.

What is const auto& in C++?

C++ auto auto, const, and references The auto keyword by itself represents a value type, similar to int or char . It can be modified with the const keyword and the & symbol to represent a const type or a reference type, respectively. These modifiers can be combined.

Do range based for loops use iterators?

Range-Based 'for' loops have been included in the language since C++11. It automatically iterates (loops) over the iterable (container). This is very efficient when used with the standard library container (as will be used in this article) as there will be no wrong access to memory outside the scope of the iterable.


If you don't want to change the items as well as want to avoid making copies, then auto const & is the correct choice:

for (auto const &x : vec)

Whoever suggests you to use auto & is wrong. Ignore them.

Here is recap:

  • Choose auto x when you want to work with copies.
  • Choose auto &x when you want to work with original items and may modify them.
  • Choose auto const &x when you want to work with original items and will not modify them.

If you have a std::vector<int> or std::vector<double>, then it's just fine to use auto (with value copy) instead of const auto&, since copying an int or a double is cheap:

for (auto x : vec)
    ....

But if you have a std::vector<MyClass>, where MyClass has some non-trivial copy semantics (e.g. std::string, some complex custom class, etc.) then I'd suggest using const auto& to avoid deep-copies:

for (const auto & x : vec)
    ....

When we don't need changing vec items, Examples suggest to use first version.

Then they give a wrong suggestion.

Why they don't suggest something which const references

Because they give a wrong suggestion :-) What you mention is correct. If you only want to observe an object, there is no need to create a copy, and there is no need to have a non-const reference to it.

EDIT:

I see the references you link all provide examples of iterating over a range of int values or some other fundamental data type. In that case, since copying an int is not expensive, creating a copy is basically equivalent to (if not more efficient than) having an observing const &.

This is, however, not the case in general for user-defined types. UDTs may be expensive to copy, and if you do not have a reason for creating a copy (such as modifying the retrieved object without altering the original one), then it is preferable to use a const &.


I'm going to be contrary here and say there is no need for auto const & in a range based for loop. Tell me if you think the following function is silly (not in its purpose, but in the way it is written):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Here, the author has created a const reference to v to use for all operations which do not modify v. This is silly, in my opinion, and the same argument can be made for using auto const & as the variable in a range based for loop instead of just auto &.


I would consider

for (auto&& o : range_expr) { ...}

or

for (auto&& o : std::as_const(range_expr)) { ...}

it always works.

and beware of possible Temporary range expression pitfalls.

In C++20 you have something like

for (T thing = foo(); auto& x : thing.items()) { /* ... */ }