
Auto Type Deduction in C++ Range-Based For Loops - jupp0r
https://blog.petrzemek.net/2016/08/17/auto-type-deduction-in-range-based-for-loops/
======
creed
I don't like that at all.

Sure if you consider:

for (auto& p : wordCount) {

    
    
        // ... word: p.first, count: p.second

}

then

for (auto& [word, count] : wordCount) { // C++1z

    
    
        // ...

}

seems like an improvement because you immediately know the semantics of the
two parts of the pair. But you have no idea what "word" or "count" _is_. In
this example with words like "word" or "count" the semantics somehow encode
type information but most of the time you deal with user defined types where
that information is a) not clear and b) not as trivial.

To me this whole "auto" thing seems misunderstood. People use it to make their
lifes "easier" (think lazy) but not simpler. The type system is there to
_help_ you and type information is of uttermost importance and should be
available as closest to the usage as possible without destroying readability
so you don't have to look it up somewhere else.

using wcPair = std::pair<const std::string, int>;

for (wcPair& p : wcMap) {

    
    
      std::cout << p.first << " : " << p.second << std::endl;

}

seems reasonable enough. The loop itself can be read without clutter, you can
infer what's supposed to happen without knowing much stuff and even if you
want a deeper understanding of what's going on then the information you need
is right above.

Doesn't that seem much better? oO

...and if you want a language _without_ a type system don't use a language
with a type system.

~~~
jschwartzi
The purpose of the type system in C++ is partly to encode semantics about the
usage of a particular construct. For example, we might declare three or four
types which all support the increment() function(or operator). The traditional
way to do this is to create an interface and have the four concrete types
inherit from the interface.

What auto says is "I don't care what type this value is. I just want it to
support the semantics that I'm about to describe in this block." If you change
the return type of the value in the auto expression, as long as the new return
type supports the same semantics as the original return type, you don't need
to touch that function after the refactor.

The compiler still complains when it can't find a way to make the return value
support the semantics you're asking for. This is an improvement, but only in
cases where you genuinely don't care what type it is, but only that it
supports iteration. You still have to use auto with care, like every other
keyword, but it does significantly improve maintainability in code bases where
traditionally you would mechanically key in the same type information multiple
times throughout the declaration of a class and its usage. Typedef and using
statements solve similar problems, but they don't solve exactly the same
problem.

For a good example of why this is a wonderful thing, try to determine what
return type you should declare for an STL iterator, or try using Boost while
avoiding the auto keyword.

~~~
clappski
I'm currently working on a code base that's just upgrading to MSVC 10.0, and
seeing std::vector::value_type(?)::const_iterator everywhere is killing me.

------
randomguy1254
I use const auto on occasion, when the element type is inexpensive to copy. If
I'm iterating over a vector if int's, why would I prefer const auto& over
const auto? Assuming the compiler does not try to optimize the const auto&,
const auto should be faster than accessing a value through a reference.

~~~
s3rvac
Using const auto& over const auto makes your code more robust. For example,
what if you later decide to change the type of items in the vector from ints
to something that is more expensive to copy? You would need to track all uses
of the vector and add an ampersand there. And as for the access speed, YMMV
but compilers are generally good at optimizations and will drop the reference
when it would be faster to just copy the items.

~~~
JoeAltmaier
But be careful about the lifetime of the object referenced! Just because its
'const' doesn't mean it cant go out of scope.

~~~
avlasyuk
Local const reference prolongs the object lifetime[1]. There shouldn't be any
problems whenever you use it in a for loop.

[1]: [https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-
th...](https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-
important-const/)

~~~
dbaupp
There definitely can be problems: the reference may escape the for loop, and
the reference may be invalidated by a modification of the container (even for
something "stable" like ordered_map, the referent may be removed).

~~~
E6300
If a pointer/reference escapes the loop you'll have problems whether the thing
to the right of the colon is a reference or a copy. If it's a copy, the
escaped reference will _definitely_ be invalid once that iteration complete.
If it's a reference, it will remain valid in more situations.

~~~
dbaupp
Yes, I believe that's what I was saying, but your comment sounds like it is
disagreeing. Could you clarify?

~~~
E6300
Oh, okay, then. I thought you were saying that that was a problem specific to
iterating by reference.

------
captainmuon
Huh, I always thought that there is an optimization to always choose the most
consty and referency option possible. So if you write `auto` but don't modify
the item in the loop, it may choose to use a reference / pointer under the
hood, as if you wrote `auto &` or `const auto &`. But it can't strip away the
`&`.

The reason is that I imagined C++ can substitute anything for auto, so it can
make `auto` into a pointer to Foo, but it can't turn `auto &` into `Foo`. (In
a first pass, of course due to the as-if rule the compiler can do whatever it
wants when you are not looking.)

In the example the author reccommends against, `for (const auto x : range)`, I
would think it means:

\- const: don't _let_ me modify x (enforce const-correctness at compile time)

\- Make a copy or reference, whatever is faster.

Particularly, I think/thought: `const` does _not_ tell the compiler to make a
copy. The lack of `&` allows the compiler to make a copy.

I'm sure this interpretation is not 100% what's in the standard, but a
intuition I developed from observation, so probably some of it is wrong.

~~~
revelation
_auto_ doesn't mean "make a copy or reference, whatever is faster". _auto_
first and foremost means "make a copy". Then sometimes compilers can do a
trick called copy-elision and you might be working on a reference. But
generally assume it's going to be a copy.

auto& means only make a reference.

------
jmaschad
Wouldn't one want to use 'f(std::forward(x))' with 'auto&&' ?

~~~
lorenzhs
I think the example with function application is a bad one here - moving the
element potentially leaves it in an undefined state. It would be a better
example if the code did something with _x_ instead of calling a function on
it.

So I don't think you'd want to forward it. Note that you also need the
element's type to forward it, which isn't possible in general with the
example's signature.

~~~
sseagull
This has been slightly confusing to me, but the syntax auto&& might not mean
an rvalue reference. According to the post, it means a universal reference. I
wish they would have made a different syntax. For example, with templates, my
understanding is:

void somefunction(int && i) // i is an rvalue reference

template<typename T> void somefunction2(T && i) // i is a universal reference

It might be a similar thing with auto&&. So I would tentatively agree that
std::forward should be used.

~~~
lorenzhs
Yes in this case it's a universal reference, I'm aware. Universal references
work with auto&&. What std::forward does is the following:

\- if you put in an rvalue reference, it's like std::move

\- if you put in anything else, it does nothing.

So my point about the article's formulation ("when you want to modify elements
in the range in generic code") and its example remains.

