c++ - with - 迭代基本類型時使用const引用的任何缺點?




template template parameter (2)

我發現自己最近越來越多地使用C ++ 11,而且在過去我會使用迭代器的地方,我現在盡可能使用基於範圍的for循環

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); }

但是如果集合元素類型是模板參數呢? foo_func()可能會被重載以通過const引用傳遞複雜(=昂貴的複制)類型,並通過值傳遞簡單的類型:

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

當我使用上面的C ++ 03風格的代碼時,我沒有多想。 我會以相同的方式迭代,因為取消引用const_iterator會產生一個const引用,一切都很好。 但是使用C ++ 11基於範圍的for循環,我需要使用const引用循環變量來獲得相同的行為:

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

突然之間我不確定了,如果auto不是一個簡單類型(例如實現引用的幕後指針),那麼這將不會引入不必要的彙編指令。

但是編譯示例應用程序確認簡單類型沒有開銷,這似乎是在模板中使用基於範圍的for循環的通用方法。 如果不是這種情況,那麼boost::call_traits::param_type就是這樣的。

問題:標準中是否有任何保證?

(我意識到這個問題與基於範圍的for循環並不真正相關。使用const_iterators時它也存在。)


6.5.4 / 1說:

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

讓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
    }
}

(進一步解釋所有__ gubbins的含義)。

當然,與直接使用*__begin而不是e inside 語句相比,該標準並不保證該行const auto &e = *__begin引入性能開銷。 允許實現通過費力地將指針複製到某個堆棧槽中來實現引用,然後在每次使用引用時將其讀回,並且不需要優化。

但是,在__begin是容器迭代器(其operator*返回引用)的情況下,沒有理由在合理的編譯器中存在開銷,然後e語句中通過值傳遞。


標準容器都返回來自迭代器的引用(但是,請注意,某些“容器不是真正的容器,例如, std::vector<bool>返回代理)。其他迭代器可能會返回代理或值,儘管這不是'嚴格支持。

當然,該標準對性能沒有任何保證。 任何與性能相關的特性(超出複雜性保證)都被認為是實施質量。

也就是說,您可能需要考慮讓編譯器像以前一樣為您做出選擇:

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

這裡的主要問題是f()可能會收到非const引用。 如果需要,可以使用const版本的coll來防止這種情況。





c++11