Ü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.