Isn't that const auto & redundant?

I found this code in one of Stroustrup books:

void print_book(const vector<Entry>& book) { for (const auto& x : book) // for "auto" see ยง1.5 cout << x << '\n'; } 

But const seems superfluous because x will be output as const_iterator , since book is const in the parameter. Is const auto really?

+44
c ++ const
Jun 03 '16 at 3:30
source share
7 answers

In my opinion, it looks obvious, and that is why it is better. Therefore, if I mean const , then I would prefer to write it explicitly. It strengthens local reasoning and, thus, helps others understand the code relatively easily; other programmers should not look at the book declaration and conclude that x is const .

+70
Jun 03 '16 at 3:35
source share

The code is not only intended for the compiler to read - it is also for people to read. Compatibility is good only when it does not come at the expense of readability.

In this code, the output constant is immediate. With the proposed change, the derived constant will require the reader to remember or view the book definition exactly.

If the reader needs to know about the constant, brevity would be counterproductive here.

+33
Jun 03 '16 at 3:50
source share

This is much clearer than the const keyword. You will immediately realize that this will not be changed if the const keyword was not there, we would not know if we had not looked at the signature of the function.

Using the const keyword is clearly much better for code readers.




Also, when you read the code below, you expect x to be modified

 for (auto& x : book) { ... } 

And then when you try to change x , you find that this leads to an error, because book is const .
In my opinion, this is not clear code. It is better to indicate in the code that x will not change so that others who read the code will correctly meet their expectations.

+14
Jun 03 '16 at 3:52
source share

Here I will approach from a different angle. const and const_iterator mean completely different things.

const applied to an iterator means that you cannot change the iterator itself, but you can change the element it points to, just like a pointer declared as int* const . const_iterator means that you cannot change the element that it points to, but you can still change the iterator itself (i.e. you can increase and decrease it) as a pointer designated as const int* .

 std::vector<int> container = {1, 2, 3, 4, 5}; const std::vector<int> const_container = {6, 7, 8, 9, 0}; auto it1 = container.begin(); const auto it2 = container.begin(); auto it3 = const_container.begin(); const auto it4 = const_container.begin(); *it1 = 10; //legal *it2 = 11; //legal *it3 = 12; //illegal, won't compile *it4 = 13; //illegal, won't compile it1++; //legal it2++; //illegal, won't compile it3++; //legal it4++; //illegal, won't compile 

As you can see, the ability to change the element that the iterator points to depends only on whether it is iterator or const_iterator , and the ability to change the iterator itself depends only on whether the variable declaration for the iterator has the qualifier const .

Edit: I just realized that this is a range, and therefore there are no iterators here (at least not explicitly). x not an iterator and is actually a direct reference to a container element. But you have the right idea, the const qualifier will be displayed regardless of whether it is explicitly written.

+7
Jun 03 '16 at 17:17
source share

I would suggest a counter point. In Herb Sutter, talk at CppCon 2014 "Back to Basics! C ++ Modern Style Basics" (timestamp 34:50), he shows this example:

 void f( const vector<int>& v) { vector<int>::iterator i = v.begin(); // error vector<int>::const_iterator i = v.begin(); // ok + extra thinking auto i = v.begin(); // ok default 

He claims that auto will work even if you change the function parameter, since it correctly outputs const or non-const . Further, on slide 36, he states: "You need to know if your variable is const / volatile or not!" as an argument about why "auto & &" is bad for local variables.

This essentially boils down to the question. Some people think that being explicit is good for readability or maintainability, but the opposite can be said: redundancy shows a lack of understanding (any of the C ++ rules or what your code really does 1 ), and can be harmful to readability and maintainability. Do whatever you think is best.

1 : This is a pretty far-fetched example, but C ++ rules do not really work in the general case and are hard to remember. For example, Herb Sutter recommends that constructor parameters pass by a value that contradicts regular function overloads. This is one of those situations where const everywhere can bite your foot, depending on whether you agree with Herb.

+4
Jun 03 '16 at 7:32
source share

Mentioning const early and often tells the code reader more about what happens if the reader does not need to look further in the code.

Now, given your code, I would write it as:

 void print_book(const std::vector<Entry>& book) { for (auto&& x : book) std::cout << x << '\n'; } 

or even:

 void print_book(std::experimental::array_view<const Entity> book) { for (auto&& x : book) std::cout << x << '\n'; } 

but just because the code is so short, I would omit the redundant const .

(the array_view version eliminates the useless dependency on the argument being vector - vector<?> const& , usually with your interface, since vector<?> const& does not provide anything useful that array_view<?> does not, but can force copy from initialization lists or from source arrays of C, etc.)

There is no reason to use const auto& over auto& or auto&& with respect to the compiler, since all 3 (in this context) are inferred with the same type. The only reason to use one or the other is to talk with other programmers.

In my case, I use auto&& by default (it says that I am iterating and I don't care what I repeat).

auto& If I know what I need to change, and auto const& if I want to emphasize that I do not change.

+2
Jun 03 '16 at 14:10
source share

this specific example is pretty simple because the input array is declared as const . In general, using const in ranged-based for can be useful both for the developer (he will get a compiler error if he tries to change the container) and for the compiler (an explicit const sometimes helps to optimize the code)

+1
Jun 03 '16 at 3:46 on
source share



All Articles