Any downside of using const reference when iterating over base types?

Recently, I have been using C ++ 11 again, and where I would have used iterators in the past, now I use a range for loops whenever possible:

std::vector<int> coll(10); std::generate(coll.begin(), coll.end(), []() { return rand(); } ); 

C ++ 03:

 for (std::vector<int>::const_iterator it = coll.begin(); it != coll.end(); ++it) { foo_func(*it); } 

C ++ 11:

 for (auto e : coll) { foo_func(e); } 

But what if the type of the collection item is a template parameter? foo_func() is likely to be overloaded to pass complex (= expensive copies) types by constant link and simple by value:

 foo_func(const BigType& e) { ... }; foo_func(int e) { ... }; 

I did not think about this when I used the C ++ 03 style code above. I would repeat the same thing, and after dereferencing, const_iterator creates a constant link, everything was fine. But, using a C ++ 11 loop for a loop, I need to use a loop contour variable to get the same behavior:

 for (const auto& e : coll) { foo_func(e); } 

And suddenly, I was no longer sure if this did not introduce unnecessary assembly instructions, if auto was a simple type (for example, a pointer behind the scene for implementing a link).

But compiling the sample application has confirmed that there is no overhead for simple types, and this seems like a general way to use ranges for loops in templates. If this were not the case, boost :: call_traits :: param_type would be correct.

Question: Are there any guarantees in the standard?

(I understand that the problem is not related to range-based loops, as well as when using const_iterators.)

+32
c ++ c ++ 11 templates
Oct 24 '12 at 20:55
source share
2 answers

Standard containers return links from their iterator (note that some โ€œcontainers are not containers, for example, std::vector<bool> , which returns a proxy). Other iterators can return proxies or values, although this is not strictly supported .

Of course, the standard makes no guarantees regarding performance. Any function related to performance (in addition to guarantees of complexity) is considered the quality of implementation.

However, you might want the compiler to make the choice for you, as before:

 for (auto&& e: coll) { f(e); } 

The main problem here is that f() can get a non const link. This can be prevented, if necessary, using the const version of coll .

+12
Oct 24
source share

6.5.4 / 1 says:

 for ( for-range-declaration : braced-init-list ) statement 

let range-init be equivalent to a list bound to init-init. In each case, a range-based statement is equivalent to

 { auto && __range = range-init; for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } } 

(further explanation of the meaning of all that __ gubbins).

The standard does not guarantee that this line const auto &e = *__begin enters performance overhead, of course, compared to directly using *__begin instead of e inside the statement. Implementations are allowed to embed links by painstakingly copying a pointer to some slot in the stack, and then reading it every time a link is used, and there is no need to optimize.

But there is no reason why there should be an overhead in a reasonable compiler if __begin is an iterator of the container (whose operator* returns the link) and then e is passed the value in the expression.

+11
Oct 24 '12 at 21:16
source share



All Articles