Kaptaki öğeleri gözlemlemek ve bunları yerinde değiştirmek arasında ayrım yapmaya başlayalım .
Elemanları gözlemleme
Basit bir örneği ele alalım:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
cout << x << ' ';
Yukarıdaki kod elemanlarını (basar ints) vector:
1 3 5 7 9
Şimdi, vektör öğelerinin sadece basit tamsayılar değil, özel kopya oluşturucu vb. İle daha karmaşık bir sınıfın örnekleri olduğu başka bir durumu düşünün.
// A sample test class, with custom copy semantics.
class X
{
public:
X()
: m_data(0)
{}
X(int data)
: m_data(data)
{}
~X()
{}
X(const X& other)
: m_data(other.m_data)
{ cout << "X copy ctor.\n"; }
X& operator=(const X& other)
{
m_data = other.m_data;
cout << "X copy assign.\n";
return *this;
}
int Get() const
{
return m_data;
}
private:
int m_data;
};
ostream& operator<<(ostream& os, const X& x)
{
os << x.Get();
return os;
}
Yukarıdaki for (auto x : v) {...}sözdizimini bu yeni sınıfla kullanırsak:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}
çıktı şöyle bir şeydir:
[... copy constructor calls for vector<X> initialization ...]
Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9
Çıktıdan okunabildiğinden, döngü yinelemeleri için aralık tabanlı sırasında kopya yapıcı çağrıları yapılır.
Biz Bunun nedeni, yakalama kaptan elemanları değeri
( auto xkısmı içindefor (auto x : v) ).
Bu, verimsiz bir koddur, örneğin, bu elemanların örnekleri ise std::string, yığın bellek tahsisi yapılabilir, bellek yöneticisine pahalı geziler vb. Yapılabilir. Bu sadece bir kaptaki elemanları gözlemlemek istiyorsak işe yaramaz .
Yani, daha iyi bir sözdizimi mevcuttur: Yakalama tarafından constreferans , yani const auto&:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}
Şimdi çıktı:
[... copy constructor calls for vector<X> initialization ...]
Elements:
1 3 5 7 9
Sahte (ve potansiyel olarak pahalı) bir kopya oluşturucu çağrısı olmadan.
Yani, gözlemleyerek bir kap (yani salt okunur erişim için) elementler, aşağıdaki sözdizimi basit için gayet ucuz-to-kopya gibi türleri int, doublevb .:
for (auto elem : container)
Aksi takdirde , işe yaramaz (ve potansiyel olarak pahalı) kopya yapıcı çağrılarını önlemek constiçin referans olarak yakalama genel durumda daha iyidir :
for (const auto& elem : container)
Kaptaki öğeleri değiştirme
Kapsayıcıdaki öğeleri aralık tabanlı kullanarak değiştirmek istiyorsak for, yukarıdaki for (auto elem : container)ve for (const auto& elem : container)
sözdizimleri yanlıştır.
Aslında, önceki durumda, orijinal öğenin elembir kopyasını saklar , böylece üzerinde yapılan değişiklikler kaybolur ve kapta kalıcı olarak saklanmaz, örneğin:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v) // <-- capture by value (copy)
x *= 10; // <-- a local temporary copy ("x") is modified,
// *not* the original vector element.
for (auto x : v)
cout << x << ' ';
Çıktı sadece başlangıç dizisidir:
1 3 5 7 9
Bunun yerine, kullanma girişimi for (const auto& x : v)derlenemez.
g ++ şöyle bir hata mesajı verir:
TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
x *= 10;
^
Bu durumda doğru yaklaşım constreferanssız olarak yakalanmaktadır :
vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
x *= 10;
for (auto x : v)
cout << x << ' ';
Çıktı (beklendiği gibi):
10 30 50 70 90
Bu for (auto& elem : container)sözdizimi daha karmaşık türler için de kullanılabilir, örneğin vector<string>:
vector<string> v = {"Bob", "Jeff", "Connie"};
// Modify elements in place: use "auto &"
for (auto& x : v)
x = "Hi " + x + "!";
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
cout << x << ' ';
çıktı:
Hi Bob! Hi Jeff! Hi Connie!
Proxy yineleyicilerinin özel durumu
Varsayalım vector<bool>ve yukarıdaki sözdizimini kullanarak öğelerinin mantıksal boole durumunu ters çevirmek istiyoruz:
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
Yukarıdaki kod derlenemedi.
g ++ aşağıdakine benzer bir hata iletisi verir:
TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
for (auto& x : v)
^
Sorun, std::vectorşablonun alanı optimize etmek için s paketleyen bir uygulamayla (her boolean değeri bir bitte, sekiz "boolean" biti bir baytta saklanması) için uzman olmasıdır .boolbool
(Tek bit başvuru döndürmek mümkün değildir beri) Bu nedenle,
vector<bool>sözde kullanır "vekil yineleyici" desen. "Proxy yineleyici", kayıttan çıkarıldığında sıradan bir sonuç vermeyenbool & , bunun yerine dönüştürülebilir bir proxy sınıfı olan geçici bir nesneyi (değere göre) döndüren bir yineleyicidir . (Ayrıca bu soruya ve ilgili cevaplara bakınız)bool StackOverflow'daki .)
Öğelerini yerinde değiştirmek vector<bool>için yeni bir tür sözdizimi (kullanma auto&&) kullanılmalıdır:
for (auto&& x : v)
x = !x;
Aşağıdaki kod iyi çalışıyor:
vector<bool> v = {true, false, false, true};
// Invert boolean status
for (auto&& x : v) // <-- note use of "auto&&" for proxy iterators
x = !x;
// Print new element values
cout << boolalpha;
for (const auto& x : v)
cout << x << ' ';
ve çıktılar:
false true true false
for (auto&& elem : container)Sözdiziminin diğer sıradan (proxy olmayan) yineleyicilerde de işe yaradığını unutmayın (örneğin a vector<int>veya avector<string> ).
(Bir yan not olarak, yukarıda belirtilen "gözlemleme" sözdizimi for (const auto& elem : container), proxy yineleyici durumu için de işe yarar.)
özet
Yukarıdaki tartışma aşağıdaki kılavuzlarda özetlenebilir:
İçin gözlem elemanları aşağıdaki sözdizimini kullanın:
for (const auto& elem : container) // capture by const reference
Nesnelerin kopyalanması ucuzsa ( ints, doubles vb.), Biraz basitleştirilmiş bir form kullanmak mümkündür:
for (auto elem : container) // capture by value
İçin modifiye yer, kullanımda unsurları:
for (auto& elem : container) // capture by (non-const) reference
Kapsayıcı "proxy yineleyicileri" kullanıyorsa (gibi std::vector<bool>), şunu kullanın:
for (auto&& elem : container) // capture by &&
Elbette , öğenin döngü gövdesi içinde yerel bir kopyasını yapmaya ihtiyaç varsa , değer ( for (auto elem : container)) ile yakalamak iyi bir seçimdir.
Genel kod hakkında ek notlar
Gelen jenerik kod biz genel tür hakkında varsayımlar yapamazsınız çünkü Tkopyasına ucuz olma, içinde gözlemleyerek modunu her zaman kullanmak güvenlidir for (const auto& elem : container).
(Bu, potansiyel olarak pahalı yararsız kopyaları tetiklemez, aynı zamanda kopyalanması ucuz türler intiçin ve ayrıca proxy yineleyicileri kullanan kaplar için de iyi çalışır std::vector<bool>.)
Ayrıca, mod değiştirme modunda, genel kodun proxy yineleyiciler durumunda da çalışmasını istiyorsak , en iyi seçenek for (auto&& elem : container).
(Bu, std::vector<int>veya gibi sıradan proxy olmayan yineleyiciler kullanan kaplar için de iyi çalışır std::vector<string>.)
Dolayısıyla, genel kodda aşağıdaki yönergeler sağlanabilir:
İçin gözlem elemanları, kullanın:
for (const auto& elem : container)
İçin modifiye yer, kullanımda unsurları:
for (auto&& elem : container)