SORU
10 NİSAN 2013, ÇARŞAMBA


C 11'un kullanarak doğru yolu aralığı tabanlı nedir?

C 11 kullanarak doğru yolu aralığı tabanlı for nedir?

Ne sözdizimi kullanılmalıdır? for (auto elem : container), ya for (auto& elem : container) for (const auto& elem : container)? Ya başka?

CEVAP
10 NİSAN 2013, ÇARŞAMBA


Hadi ayırt etme başlargözlemleyerekbu continer öğeleri vsdeğiştirmeyerde.

Öğeleri gözlemleyerek

Hadi 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ı (ints) vector baskı:

1 3 5 7 9

Şimdi, hangi vektör öğeleri sadece basit tamsayı değil,başka bir durumda, düşünün ama daha karmaşık bir sınıf, özel örneklerini yapıcı, vb kopyalayı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ıda kullanırsak for (auto x : v) {...} Bu yeni sınıf ile kullanımı:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (auto x : v)
{
    cout << x << ' ';
}

çıktı gibi bir şey

[... 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ısı okunabilir gibikurucu kopyalayınaramalar sırasında yapılır döngü yineleme aralığı tabanlı.
Bu gibidiryakalamakapsayıcı öğelerideğer (for (auto x : v) 39 *kısmı).

Buverimsizeğer bu unsurlar std::stringörnekleri kod, örneğin, yığın bellek ayırma, bellek yöneticisi, vb için pahalı gezileri ile yapılabilir. Bu eğer biz sadece, işe yaramazinceleyinbir kap içindeki elemanlar.

Bu yüzden, daha iyi bir sözdizimi kullanılabilir: yakalamakconst başvuruyaniconst 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

Olmadan herhangi bir sahte (ve büyük olasılıkla pahalı) kurucu çağrı kopyalayın.

Bu yüzden, ne zamangözlemleyerekbir kap (yani salt okunur erişim) elemanları, aşağıdaki sözdizimi basit için gayet iyiucuz-kopyatürleri, int, double vb gibi.:

for (auto elem : container) 

Başka const referans tarafından yakalama daha iyi olurgenel durum, işe yaramaz (ve potansiyel olarak pahalı) önlemek için yapıcı çağrıları kopyalayın:

for (const auto& elem : container) 

Değiştirme kapsayıcı öğeleri

Eğer istersekdeğiştirinbir kap aralığı tabanlı forkullanarak öğeleri yukarıdaki for (auto elem : container) for (const auto& elem : container) sözdizimi yanlış.

Eski durumda, elem aslında saklarkopyalaorijinal eleman, değişiklikler yapılması için sadece kaybolur ve saklanır ısrarla yani konteyner, ö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ıç sırası:

1 3 5 7 9

Bunun yerine, for (const auto& x : v) kullanma girişimi sadece derlemek için başarısız olur.

g böyle bir şey: bir hata mesajı verir

TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
          x *= 10;
            ^

Bu durumda doğru yaklaşım olmayanconst referans tarafından yakalanıyor

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

for (auto& elem : container) bu söz dizimini de daha karmaşık türleri için çalışır, örneğin vector<string> da dikkate alınarak

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 kullanımına özel bir durum

vector<bool> ve biz bir mantıksal boolean durumu ters çevirmek istiyorum varsayalım öğeleri, yukarıdaki sözdizimini kullanarak:

vector<bool> v = {true, false, false, true};
for (auto& x : v)
    x = !x;

Yukarıdaki kodu derlemek için başarısız olur.

g bir hata iletisi şuna benzer sonuçları:

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 şablon olduğunuuzmanbir bool için uygulama bupaketleribool(her bir boolean değeri optimize alana s bir süre sonra, "boolean" byte bit). sekiz saklı

Bu mümkün tek bir bit için bir başvuru döndürmek için değil beri) nedeniyle, vector<bool> bir sözde kullanır< . "proxy yineleyici"desen. Bir "proxy yineleyici" başvuru yapıldı, o kadar büyük bir yineleyicideğilyeld bir bool & ama bunun yerine sıradan döndürür (değer)geçici bir nesne, proxy class convertible to bool. (102 ** StackOverflow burada.)

Yerde vector<bool> sözdizimi yeni bir tür unsurları (auto&& kullanarak) değiştirmek için kullanılmalıdır:

for (auto&& x : v)
    x = !x;

Aşağıdaki kod gayet 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 çıkışlar:

false true true false

Bu notufor (auto&& elem : container)sözdizimi diğer durumlarda çalışır sıradan (vekil olmayan) kullanımına (vector<int> vector<string> gibi).

(Bir yan not, söz konusu "" for (const auto& elem : container) sözdizimi iyi de proxy yineleyici dava için çalışır.) gözlemleyerek

Özet

Yukarıdaki tartışma kılavuz çizgileri aşağıdaki şekilde özetlenebilir:

  1. İçingözlemleyerekelemanları, aşağıdaki sözdizimini kullanın:

    for (const auto& elem : container)    // capture by const reference
    
    • Eğer nesneleridirucuz kopyalamak için(ints doubles, vb.) gibi, mümkünse biraz basitleştirilmiş bir formu kullanmak için:

      for (auto elem : container)    // capture by value
      

  2. İçindeğiştirmeunsurlar yerli yerinde, kullanın:

    for (auto& elem : container)    // capture by (non-const) reference
    
    • Eğer kap kullanır< . em ^"proxy kullanımına"(std::vector<bool> gibi), kullanın:

      for (auto&& elem : container)    // capture by &&
      

Eğer bir ihtiyaç varsa tabiiyerel kopyadöngü gövdesi içinde elemanın, yakalamadeğer(for (auto elem : container)) iyi bir seçimdir.


Genel kod üzerinde ek notlar

genel kodgenel tür hakkında varsayımlar yapamayız beri T kopyalamak için ucuz olmakgözlemleyerekmod güvenli her zaman kullanmak içinfor (const auto& elem : container).
(Bu olmayacak tetikleyici potansiyel olarak pahalı işe yaramaz kopyaları, olacak iş gayet de ucuz-kopyalama türleri gibi int, ve aynı zamanda için kaplar proxy kullanımına, gibi std::vector<bool>.)

Ayrıca, içindedeğiştirmeeğer istersek modugenel kodayrıca proxy kullanımına durumda çalışmak için en iyi seçenektirfor (auto&& elem : container).
(Bu sadece iyi konteynerlerin sıradan olmayan proxy kullanımına, std::vector<int> veya std::vector<string>. gibi) kullanmak için çalışacak

Yani, içindegenel kodaşağıdaki yönergeleri temin edebilirsiniz

  1. İçingözlemleyereköğeleri, kullanın:

    for (const auto& elem : container)
    
  2. İçindeğiştirmeunsurlar yerli yerinde, kullanın:

    for (auto&& elem : container)
    

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Bryan Adams

    Bryan Adams

    30 Mart 2006
  • Chuck Testa

    Chuck Testa

    14 AĞUSTOS 2011
  • SVB International

    SVB Internat

    29 EKİM 2011