Yanıtlar:
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:SomeFunctor
const&
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, SomeFunctor
vektörün elemanlarını değiştiremeyen sözdizimsel güvencelerimiz var (elbette bir sabitleme olmadan). Biz açıkça const_iterator
s 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 .
std::cbegin/cend
serbest işlev std::begin/std::end
yoktur. Bu komite tarafından bir denetimdi. Bu işlevler mevcut olsaydı, genellikle bunları kullanmanın yolu olurdu.
std::cbegin/cend
C ++ 14'te eklenecek. Bkz. En.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
eşittir for(const auto &item : vec)
?
const
başvuruyu koyarak değiştirilmeyeceğini söylüyor . Nicol, kabı const olarak görür, bu yüzden auto
bir const
referans çıkarır . IMO auto const& item
daha kolay ve nettir. Burada neden std::as_const()
iyi olduğu belli değil ; Kullanılan const
kodu kontrol edemediğimiz jenerik kod olmayan bir şey geçirirken yararlı olacağını görebiliyorum , ancak range- ile for
yapabiliriz, bu yüzden orada bana gürültü eklenmiş gibi görünüyor.
Nicol Bolas'ın cevabında söylediklerinin ötesinde , yeni auto
anahtar 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();
const_iterator
sadece başka bir tanımlayıcıdır. Her iki sürüm de normal üye typedefs decltype(container)::iterator
veya aramasını kullanmaz decltype(container)::const_iterator
.
const_iterator
vardır auto
: make_const
Nesne bağımsız değişkenini nitelemek için çağrılan bir yardımcı işlev şablonu yazın .
Bunu pratik bir kullanım alanı olarak kabul edin
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
Sabit it
olmayan bir yineleyici olduğu için atama başarısız oluyor . Başlangıçta cbegin kullandıysanız, yineleyici doğru tipte olurdu.
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.
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
iterator
ve const_iterator
kalı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)
{
// ...
}
const
Baş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) auto
bunu bir sorun yaratmaz; (B) performans hakkında konuşurken, burada yapmanız gereken ana şeyi kaçırdınız: döngüyü end
başlatma koşulunda bir kopyasını bildirerek yineleyiciyi önbelleğe alın ve for
yeni bir kopya almak yerine bununla karşılaştırın her yineleme için değer. Bu daha iyi olur. : P
const
, const
anahtar 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 .
const
(neredeyse dolaylı olarak) performans faydaları sağlayabilirsiniz açık yapıyordu ; sadece bu biri okuma durumunda " const
oluşturulan kod hiç bir şekilde etkilenmezse ekleme zahmetine" düşünebilirsiniz , bu doğru değil.
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.