2016-04-13 16 views
2

C++ 'da lambda işleviyle ilgili bir sorunum var: Giriş olarak dize listesi verilen bir nesne dizisini dolduran bir eşzamansız yükleyici tanımlamaya çalışıyorum.C++ 11 lambda referansa erişemiyor

kodu (tam olarak değil ama anladınız umarım) şöyle görünür:

void loadData() { 
    while (we_have_data()) { 
     std::string str = getNext(); 
     array.resize(array.size() + 1); 
     element &e = array.back(); 
     tasks.push_back([&, str]() { 
      std::istringstream iss(str); 
      iss >> e; 
     } 
    } 

    for (auto task: tasks) { 
     task(); 
    } 
} 

sonunda ben görevlerin listesini tarayın ve ilk erişim, uygulama çöker onları idam zaman lambda içindeki değişkene. Bir hata ayıklayıcının içinde çalışırsam, nesnenin kendi içinde doğru değerleri bulabilirim. Yanlış bir şey yapıyorum ama aslında ne olduğunu anlamıyorum.

+1

, 'array' bir 'std :: vector' yer değiştirmesi mi? –

cevap

0

Burada iki suçlama var. Öncelikle, bir yineleyiciye yeniden boyutlandırılması ve yeniden yerleştirilmesi muhtemel bir vektöre bir başvuru yakalıyorsunuz demektir. Üste Geri Bildirim Ver Daha fazla bilgi Daha fazla bilgi için bkz: http://www.microsoft.com/downloads/default.aspx?displaylang=tr İkinci olarak, kapsam dışında kalan bir yerel (yığın) değişkenine bir başvuru yakalıyorsunuz demektir. Döngü içinde, derleyici muhtemelen her seferinde 'e' için aynı bellek konumunu kullanır, bu nedenle tüm referanslar aynı yığın konumuna işaret eder.

Daha basit bir çözüm eleman numarasını saklamak olacaktır:

while (we_have_data()) { 
    std::string str = getNext(); 
    size_t e = array.size(); 
    array.resize(e + 1); 
    tasks.push_back([&, e, str]() { 
     std::istringstream iss(str); 
     iss >> array[e]; 
    } 
} 

C++ 14 ve dizeleri, göz önünde uzun isteyebilirsiniz edilir varsa: ait

tasks.push_back([&, e, str{std::move(str)}]() { 

Tüm Bu, dizilerin daha fazla manipülasyona girmeyeceğini veya görevler çalışırken kapsam dışı kalacağını varsayar.

+0

'str {std :: move (str)}' C++ 14 nedir? –

+1

Bir "vektör" (veya standart bir kapsayıcı) olduğu varsayılırsa, '.back()' bir yineleyici değil son öğeye bir başvuru sağlar. – Niall

+0

ve bir * yerel referans değişkeni * yakalanmadı –

4

Sarkan referans tutuyorsunuz. Eğer

tasks.push_back([&, str]() { 
    std::istringstream iss(str); 
    iss >> e; 
} 

yaptığınızda Sen e için bir referans beri array.back() tarafından döndürülen eleman aslında e atıfta ne olursa olsun bir referanstır referans olarak yakalar. Ne yazık ki resize, while döngüsünde çağrılır, böylece array yeniden boyutlandırılır, back() başvuruları geçerliliğini yitirir ve artık varolmayan bir nesneye başvurmaktasınız.

+0

'[&]' referansa referans olarak değil, array.back() 'için bir referans yakalar, sanırım ve dizi yeniden boyutlandırıldığında sarkmaya başlarsa, bence –

+0

@PiotrSkotnicki Evet bunu fark ettim. Cevabı güncelledim. – NathanOliver

4

element& e'un kapsamı while -loopdur.

while -loop'un her yinelemesinden sonra, kapsam dışı olan tüm e'lara ait yakalanmış bir başvurusu olan lambda işlevleriniz vardır.

+0

NathanOliver tarafından Sniped –

2

Sen e (aka. array.back()) yakalamak "by-referans" lambda oluştururken, array müteakip boyutlandırır ile (mümkünse reallocations birlikte), ucunda başvuru bırakır ve bu da hataya neden oluyor bu erişmeye çalıştığınızda sarkan referans. Dizi bir yeniden boyutlandırma (ve yeniden ayrıştırma) geçirdikten sonra daha önce atanmış bir referansla array'daki öğelere erişmek için (lambda ile sınırlı değil) herhangi bir girişim "sarkan referans" sorununa neden olur.

Alternatif olarak, iki döngü yerine, yalnızca görevi yalnızca while döngüsünde yürütme ve sarkan başvurudan vazgeçme ve işaretçi veya yineleyici tabanlı alternatifler almaya çalışma.

Ayrıca, alternatif ... array öğeler paylaşılabilir eğer işe yarayabilir bir std::shared_ptr çözüm, uyarı böylece lambda array olanlarda elemanların hisse sahipliğini paylaştı sağlanması değeriyle shared_ptr unsurları yakalamak için olacaktır yeniden boyutlandırıldıkça.

fikrinin bir örnekleme ...

void loadData() { 
    while (we_have_data()) { 
     std::string str = getNext(); 
     array.resize(array.size() + 1); 
     std::shared_ptr<element> e = array.back(); 
     tasks.push_back([e, str]() { 
      std::istringstream iss(str); 
      iss >> *e; 
     } 
    } 

    for (auto task: tasks) { 
     task(); 
    } 
}