2012-10-24 11 views
32

Kendimi daha son zamanlarda C++ 11 daha kullanarak bulmak ve geçmişte iterators kullanıyoruz nerede, şimdi range-based for loops kullanıyorum Mümkün:Temel türler üzerinde yineleme yaparken const başvuru kullanmanın herhangi bir dezavantajı var mı?

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

C++ 03:

C++ 11

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

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

Ama ne toplama elemanı tipi bir şablon parametresi ise? Ben C++ 03 kullanıyordum ederken

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

Bu kadar düşünce vermedi: foo_func() muhtemelen değerine göre const referans olarak kompleks (= kopyalamak için pahalı) türlerini ve basit olanları geçmek aşırı yüklenecektir yukarıdaki tarz kodu. Aynı şekilde yinelemeliyim, ve dereferencing bir const_iterator bir const referansı ürettiğinden, her şey yolundaydı. Ama C++ 11 aralık tabanlı döngü için ben aynı davranışı elde etmek için bir const referans döngü değişkeni kullanmak zorunda kullanarak: Bu tanıtmak olmaz ise, artık

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

Ve birden emin değildi Gereksiz montaj talimatları auto basit bir türse (referansı uygulamak için sahne arkası işaretçisi gibi).

Ancak, örnek bir uygulama derlemek, basit türler için ek yük olmadığını doğruladı ve bu, şablonlardaki döngüler için aralık temelini kullanmanın genel yolu gibi görünüyor. Bu durum böyle olmasaydı, boost::call_traits::param_type gitmek için yol olurdu.

Soru: Standartta herhangi bir garanti var mı?

(Konunun pek döngüler için tabanlı aralığı ile ilişkili olmadığını biliyoruz. Const_iterators kullanırken Ayrıca hediyesi.)

cevap

12

standart konteynerler ancak onların yineleyici (not, tüm iade başvuruları, bazı o " kapsayıcılar gerçekten kapsayıcı değildir, örneğin, bir proxy döndüren std::vector<bool>. Diğer yineleyiciler, kesinlikle desteklenmese de proxy'leri veya değerleri döndürebilirler.

Elbette, standart, performansla ilgili herhangi bir garanti vermez. Performansla ilgili her türlü özellik (karmaşıklık garantilerinin ötesinde) uygulama kalitesi olarak kabul edilir.

012 Daha önce yaptığı gibi, sen derleyici sahip düşünebilirsiniz söyledi

sizin için seçim yapmak: Burada

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

ana konu f() olmayan bir const referansı alabilirsiniz olmasıdır. Gerekirse constcoll sürümü kullanılarak önlenebilir.

+1

Ya da sadece 'auto const &'. ;'u kullanarak – Xeo

+1

@Xeo: Yineleyici, bir değere [' const'] referansı yerine bir değer verirse, "T & amp;" değeri, bir referanstan ziyade değeri çıkarır. değer. –

+0

* Huh? * 'Auto &&, her zaman bir referans olur, yineleyiciler ne olursa olsun. Gerçekten bir değer verirse, bu sadece bir referans olacaktır. (Not: Yorumunuzu yanlış anlamış olabilirim.) – Xeo

11

6.5.4/1 der ki:

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

let aralığı-init hazırladı-init-listeye eşdeğer.Her iki durumda da, bir dizi-bazlı deyimi için (daha fazla açıklama tüm __ gubbins anlamlarından aşağıdaki)

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

eşdeğerdir . Bu hat const auto &e = *__begin elbette bir performans yükü getirmektedir olmadığını

standart doğrudan deyimi içindeki *__begin yerine e kullanılarak karşılaştırıldığında herhangi garantiler yapmaz. Uygulamaların, bir işaretçiyi bazı yığın yuvalarına zahmetli bir şekilde kopyalayarak ve sonra referansın her kullanılmasında geri okuyarak ve en iyileştirmek için gerekli olmadıkça, referansları uygulamalarına izin verilir.

Ama mantıklı bir derleyici bir havai olması için hiçbir neden __begin (operator* bir başvuru verir) bir kap yineleyici ve daha sonra eaçıklamada değeri geçtiği durumda, var.

+0

Teşekkürler Steve. Peki, döngü için genel bir aralık tabanlı nasıl yazarsın? –

+0

@Daniel: Bence (const auto & e: coll) {foo_func (e); } 'gayet iyi, fakat C++ 11'i, benim için dikte eden bir stil rehberi yazabileceğini iddia etmeye yetecek kadar kullanmadım. "Boost :: call_traits :: param_type" işlevinin kullanımı, bir parametreyi geçirdikten sonra, küçük bir nesne türü yerine bir başvuru kullanmanın olası bir yük olduğunu, çünkü bazı şeyleri kısıtlayan bir çağrı sözleşmesi var. Bir fonksiyon içinde, referans değişkenlerini "saf" takma adlar olarak ele almak için derleyiciye güveniyorum. Eğer '* __ begin', değere göre geri dönerse, kontrol etmeden ekstra bir geçici durumdan kaçınmaktan bahsetmezdim. –

İlgili konular