迭代基本类型时使用const引用的任何缺点?

Dan*_*ger 33 c++ templates c++11

我发现自己最近越来越多地使用C++ 11,而且过去我会使用迭代器,我现在尽可能使用基于范围的for循环:

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

C++ 03:

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

C++ 11:

for (auto e : coll) { foo_func(e); }
Run Code Online (Sandbox Code Playgroud)

但是,如果集合元素类型是模板参数呢?foo_func()可能会重载以通过const引用传递复杂(=昂贵的复制)类型,并通过值传递简单的类型:

foo_func(const BigType& e) { ... };
foo_func(int e) { ... };
Run Code Online (Sandbox Code Playgroud)

当我使用上面的C++ 03风格的代码时,我没有多想.我会以相同的方式迭代,因为取消引用const_iterator会产生一个const引用,一切都很好.但是使用C++ 11基于范围的for循环,我需要使用const引用循环变量来获得相同的行为:

for (const auto& e : coll) { foo_func(e); }
Run Code Online (Sandbox Code Playgroud)

突然间我不再确定了,如果这auto是一个简单的类型(例如实现引用的幕后指针),这将不会引入不必要的汇编指令.

但是编译示例应用程序确认了简单类型没有开销,这似乎是在模板中使用基于范围的for循环的通用方法.如果不是这种情况,那么boost :: call_traits :: param_type就是这样的.

问题:标准中是否有任何保证?

(我意识到这个问题与基于范围的for循环并不真正相关.当使用const_iterators时它也存在.)

Die*_*ühl 13

标准容器都返回来自迭代器的引用(但是,请注意,某些"容器实际上不是容器,例如,std::vector<bool>返回代理).其他迭代器可能会返回代理或值,尽管这不是严格支持的.

当然,该标准对性能没有任何保证.任何与性能相关的特性(超出复杂性保证)都被认为是实施质量.

也就是说,您可能需要考虑让编译器像以前一样为您做出选择:

for (auto&& e: coll) { f(e); }
Run Code Online (Sandbox Code Playgroud)

这里的主要问题是f()可能会收到非const参考.如果需要,可以使用const版本来防止这种情况coll.

  • 或者...只是使用“auto const&amp;”。;) (2认同)
  • @Daniel:“auto const&amp;&amp;”不会是通用引用,只会绑定到右值。 (2认同)

Ste*_*sop 11

6.5.4/1说:

for ( for-range-declaration : braced-init-list ) statement
Run Code Online (Sandbox Code Playgroud)

让range-init等同于braced-init-list.在每种情况下,基于范围的for语句等同于

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

(进一步解释所有__gubbins 的含义).

当然,与直接使用而不是使用内部语句相比,该标准不保证该行是否会const auto &e = *__begin引入性能开销.允许实现通过费力地将指针复制到某个堆栈槽中来实现引用,然后在每次使用引用时将其读回,并且不需要优化.*__begine

但是没有理由在合理的编译器中存在开销,在__begin容器迭代器(operator*返回引用)的情况下,然后e通过语句中的值传递.