Üye işlevi içindeki lambda yakalama listesinde üye değişkeni kullanma


145

Aşağıdaki kod gcc 4.5.1 ile derlenir, ancak VS2010 SP1 ile derlenmez:

#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <set>
#include <algorithm>

using namespace std;
class puzzle
{
        vector<vector<int>> grid;
        map<int,set<int>> groups;
public:
        int member_function();
};

int puzzle::member_function()
{
        int i;
        for_each(groups.cbegin(),groups.cend(),[grid,&i](pair<int,set<int>> group){
                i++;
                cout<<i<<endl;
        });
}
int main()
{
        return 0;
}

Bu hata:

error C3480: 'puzzle::grid': a lambda capture variable must be from an enclosing function scope
warning C4573: the usage of 'puzzle::grid' requires the compiler to capture 'this' but the current default capture mode does not allow it

Yani,

1> hangi derleyici haklı?

2> VS2010'da lambda içindeki üye değişkenleri nasıl kullanabilirim?


1
Not: Olmalı pair<const int, set<int> >, bu bir haritanın gerçek çift türü. Muhtemelen bir const referansı da olmalıdır.
— Xeo

İlişkili; very helpful: thispointer.com/…
— Gabriel Staples

Yanıtlar:


157

VS2010 bu kez doğru olduğuna inanıyorum ve ben standart kullanışlı olup olmadığını kontrol ediyorum, ama şu anda yok.

Şimdi, tam olarak hata mesajının söylediği gibi: Lambda'nın kapsamı dışında bir şey yakalayamazsınız. † grid kapalı kapsamda değildir, ancak this( gridaslında her erişim this->gridüye işlevlerinde olduğu gibi gerçekleşir ). thisHemen kullanacağınız ve kopyalamak istemediğiniz için, kullanıcı tabanınız için yakalama çalışırgrid

auto lambda = [this](){ std::cout << grid[0][0] << "\n"; }

Ancak, ızgarayı depolamak ve puzzlenesnenizin zaten yok edilmiş olabileceği daha sonra erişmek için kopyalamak istiyorsanız, ara, yerel bir kopya oluşturmanız gerekir:

vector<vector<int> > tmp(grid);
auto lambda = [tmp](){}; // capture the local copy per copy

† Basitleştiriyorum - "kapsama ulaşmak" için Google veya tüm kanlı ayrıntılar için §5.1.2'ye bakın.


1
Benim için oldukça sınırlı görünüyor. Bir derleyicinin neden böyle bir şeyi önlemesi gerektiğini anlamıyorum. Bağlama ile iyi çalışır, ancak sözdizimi ostream sol kaydırma operatörü ile korkunçtur.
— Jean-Simon Brochu

3
Could tmpbir olmak const &üzere gridkopyalama azaltmak için? Hala en az bir kopya, lambda ( [tmp]) içine kopya istiyoruz , ancak ikinci bir kopyaya gerek yok.
— Aaron McDaid

4
Çözüm, gridmuhtemelen optimize edilmiş olmasına rağmen gereksiz bir ekstra kopya oluşturabilir . Daha kısa ve daha iyi: auto& tmp = grid;vb.
— Tom Swirly

4
Kullanılabilir C ++ [grid = grid](){ std::cout << grid[0][0] << "\n"; }
— 14'ünüz varsa

Gcc 4.9 (ve bu konu için gcc 5.4) ile düzeltilmiş gibi görünüyorerror: capture of non-variable ‘puzzle::grid’
— BGabor

108

Alternatiflerin özeti:

yakalama this:

auto lambda = [this](){};

üyeye yerel bir başvuru kullanın:

auto& tmp = grid;
auto lambda = [ tmp](){}; // capture grid by (a single) copy
auto lambda = [&tmp](){}; // capture grid by ref

C ++ 14:

auto lambda = [ grid = grid](){}; // capture grid by copy
auto lambda = [&grid = grid](){}; // capture grid by ref

örnek: https://godbolt.org/g/dEKVGD


5
Sadece yakalamanın başlatıcı sözdizimi ile açıkça kullanılması ilginçtir (yani C ++ 14'te sadece yapmak [&grid]hala işe yaramaz). Bunu bildiğim için çok mutluyum!
— ohruunuruus

1
İyi özet. C ++ 14 sözdizimini çok uygun
— buluyorum

22

İnanıyorum, yakalamanız gerekiyor this.


1
Bu doğru, bu işaretçiyi yakalayacak ve yine de griddoğrudan başvurabilirsiniz . Sorun, ızgarayı kopyalamak istiyorsanız ne olacak? Bu, bunu yapmanıza izin vermez.
— Xeo

9
Yapabilirsiniz, ancak sadece dolambaçlı bir şekilde: Yerel bir kopya oluşturmanız ve bunu lambda'da yakalamanız gerekir . Bu sadece lambdaların kuralı, kapalı alanın dışında sert bir şekilde yakalayamazsınız.
— Xeo

Tabii kopyalayabilirsiniz. Elbette kopyalayamazsın demek istedim.
— Michael Krelin - hacker

Açıkladığım şey, ara yerel kopya aracılığıyla bir kopya yakalama yapıyor - cevabımı görün. Bunun dışında bir üye değişkeni yakalamak için herhangi bir yol bilmiyorum.
— Xeo

Elbette, yakalamayı kopyalar, ancak üye değil. Derleyici normalden daha akıllı olmadıkça iki kopyadan oluşuyor sanırım.
— Michael Krelin - hacker

14

Lambda'nın bütüne erişmesini sağlamak yerine kapsamını sınırlayan alternatif bir yöntem this, üye değişkene yerel bir referansta bulunmaktır, ör.

auto& localGrid = grid;
int i;
for_each(groups.cbegin(),groups.cend(),[localGrid,&i](pair<int,set<int>> group){
            i++;
            cout<<i<<endl;
   });

Fikrini seviyorum: sahte bir referans değişkeni kullanarak ve yakalama listesine geçmek :)
— Emadpres
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.