Cbegin / cend'in ardındaki neden nedir?


Yanıtlar:


228

Oldukça basit. Diyelim ki bir vektörüm var:

std::vector<int> vec;

Bazı verilerle dolduruyorum. Sonra bazı yineleyiciler almak istiyorum. Belki onlarý dolaţtýr. Belki std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor());

C ++ 03, aldığı parametreyi değiştirmekteSomeFunctor serbestti . Tabii, değerine göre ya tarafından parametreyi alabilir , ama hiçbir yolu yoktur sağlamak öyle söyledi. Böyle aptalca bir şey yapmadan olmaz:SomeFunctorconst&

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

Şimdi, tanıtıyoruz cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

Şimdi, SomeFunctorvektörün elemanlarını değiştiremeyen sözdizimsel güvencelerimiz var (elbette bir sabitleme olmadan). Biz açıkça const_iterators alırız ve bu nedenle SomeFunctor::operator()çağrılır const int &. Parametreleri olduğu gibi alırsa int &, C ++ bir derleyici hatası verir.


C ++ 17 bu soruna daha şık bir çözümü vardır: std::as_const. En azından menzil tabanlı kullanıldığında zarif for:

for(auto &item : std::as_const(vec))

Bu const&, sağlandığı nesneye yalnızca a değerini döndürür .


1
Yeni protokolün vec.cbegin () yerine cbegin (vec) olduğunu düşündüm.
Kaz Dragon

20
@Kaz: Var olan hiçbir std::cbegin/cendserbest işlev std::begin/std::endyoktur. Bu komite tarafından bir denetimdi. Bu işlevler mevcut olsaydı, genellikle bunları kullanmanın yolu olurdu.
Nicol Bolas

20
Görünüşe göre, std::cbegin/cendC ++ 14'te eklenecek. Bkz. En.cppreference.com/w/cpp/iterator/begin
Adi Shavit

9
@NicolBolas for(auto &item : std::as_const(vec))eşittir for(const auto &item : vec)?
luizfls

9
@luizfls Evet. Kodunuz, öğenin constbaşvuruyu koyarak değiştirilmeyeceğini söylüyor . Nicol, kabı const olarak görür, bu yüzden autobir constreferans çıkarır . IMO auto const& itemdaha kolay ve nettir. Burada neden std::as_const()iyi olduğu belli değil ; Kullanılan constkodu kontrol edemediğimiz jenerik kod olmayan bir şey geçirirken yararlı olacağını görebiliyorum , ancak range- ile foryapabiliriz, bu yüzden orada bana gürültü eklenmiş gibi görünüyor.
underscore_d

66

Nicol Bolas'ın cevabında söylediklerinin ötesinde , yeni autoanahtar kelimeyi düşünün :

auto iterator = container.begin();

İle auto, begin()sabit olmayan bir kap başvurusu için sabit bir işleç döndürdüğünden emin olmanın bir yolu yoktur . Şimdi yapıyorsun:

auto const_iterator = container.cbegin();

2
@allyourcode: Yardımcı olmuyor. Derleyiciye, const_iteratorsadece başka bir tanımlayıcıdır. Her iki sürüm de normal üye typedefs decltype(container)::iteratorveya aramasını kullanmaz decltype(container)::const_iterator.
aschepler

2
@aschepler İkinci cümleyi anlamadım, ama sanırım sorumda "otomatik" in önünde "const" u kaçırdınız. Otomatik ne olursa olsun, const_iterator sabit olmalı.
allyourcode

26
@allyourcode: Bu size sabit olan bir yineleyici verir, ancak bu yineleyiciden sabit veriden çok farklıdır.
aschepler

2
Aşağıdakileri elde etmenizi sağlamanın basit bir yolu const_iteratorvardır auto: make_constNesne bağımsız değişkenini nitelemek için çağrılan bir yardımcı işlev şablonu yazın .
Columbo

17
Belki sadece C ++ zihniyetinde değilim, ama "basit bir yol" ve "yardımcı fonksiyon şablonu yazma" kavramları arasında bir bağlantı göremiyorum. ;)
Stefan Majewsky

15

Bunu pratik bir kullanım alanı olarak kabul edin

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

Sabit itolmayan bir yineleyici olduğu için atama başarısız oluyor . Başlangıçta cbegin kullandıysanız, yineleyici doğru tipte olurdu.


8

Gönderen http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :

böylece bir programcı sabit olmayan bir kaptan bile doğrudan bir const_iterator elde edebilir

Bu örneği verdiler

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

Bununla birlikte, bir konteynır geçişi sadece muayene için tasarlandığında, derleyicinin sabit doğruluk ihlallerini teşhis etmesine izin vermek için bir const_iterator kullanmak genellikle tercih edilen bir uygulamadır.

Çalışma belgesinin ayrıca şu anda sonlandırılmış std::begin()ve std::end()yerel dizilerle çalışan bağdaştırıcı şablonlarından da bahsettiğini unutmayın . Bu zamana karşılık gelen std::cbegin()ve std::cend()merakla eksik, ama onlar da eklenebilir.


5

Sadece bu soru üzerine tökezledi ... biliyorum bu cevap aldy ve sadece bir yan düğüm ...

auto const it = container.begin() o zaman farklı bir tip auto it = container.cbegin()

için fark int[5](i biliyorum işaretçi kullanarak, başlangıç ​​yöntemi yok ama güzel bir fark göstermek ... ama c ++ 14 için işe yarayacak std::cbegin()ve std::cend()bu aslında ne zaman burada ne kullanmalısınız) ...

int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers);      // type is int const* -> value is const

2

iteratorve const_iteratorkalıtım ilişkisi varsa ve diğer türle karşılaştırıldığında veya başka türle ilişkilendirildiğinde örtük bir dönüşüm gerçekleşir.

class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
    // ...
}

Kullanılması cbegin()ve cend()bu durumda performans artacaktır.

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}

constBaşlıca faydası performans (yani: anlamsal olarak doğru ve güvenli kod) değil, yineleyicileri başlatırken ve karşılaştırırken dönüşümden kaçınarak performansın kurtarıldığı anlamına geldiğimi fark etmem biraz zaman aldı . Ancak, bir noktanız olsa da, (A) autobunu bir sorun yaratmaz; (B) performans hakkında konuşurken, burada yapmanız gereken ana şeyi kaçırdınız: döngüyü endbaşlatma koşulunda bir kopyasını bildirerek yineleyiciyi önbelleğe alın ve foryeni bir kopya almak yerine bununla karşılaştırın her yineleme için değer. Bu daha iyi olur. : P
underscore_d

@underscore_d const, constanahtar kelimenin kendisindeki sihir nedeniyle değil, verilerin değiştirilmeyeceğini bildiği takdirde derleyici bazı optimizasyonları etkinleştirebildiğinden , daha iyi performans elde edilmesine kesinlikle yardımcı olabilir, aksi takdirde mümkün olmaz. Bunun canlı bir örneği için Jason Turner'ın konuşmasından bu biti kontrol edin .
brainplot

@brainplot Yapamayacağını söylemedim. Bunun ana yararı olmadığını ve gerçek faydası semantik olarak doğru ve güvenli bir kod olduğunda abartıldığını düşünüyorum dedim.
underscore_d

@underscore_d Evet, buna katılıyorum. Ben sadece const(neredeyse dolaylı olarak) performans faydaları sağlayabilirsiniz açık yapıyordu ; sadece bu biri okuma durumunda " constoluşturulan kod hiç bir şekilde etkilenmezse ekleme zahmetine" düşünebilirsiniz , bu doğru değil.
brainplot

0

basit, cbegin sabit bir yineleyici döndürür, burada start sadece bir yineleyici döndürür

daha iyi anlamak için burada iki senaryo alalım

senaryo - 1:

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.begin();i< v.end();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

Bu çalışacaktır çünkü burada yineleyici i sabit değildir ve 5 ile artırılabilir.

Şimdi cbegin kullanalım ve bunları sürekli yineleyiciler senaryosu olarak gösterelim - 2:

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.cbegin();i< v.cend();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

bu işe yaramayacaktır, çünkü cbegin ve cend kullanarak değeri güncelleyemezsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.