Bunu neden referansla ('& this') lambda'da yakalayamıyorum?


91

Bir lambda'da yakalamanın this(nesne özelliklerini değiştirmek için) doğru yolunun aşağıdaki gibi olduğunu anlıyorum :

auto f = [this] () { /* ... */ };

Ama şu gördüğüm tuhaflığı merak ediyorum:

class C {
    public:
        void foo() {
            // auto f = [] () { // this not captured
            auto f = [&] () { // why does this work?
            // auto f = [&this] () { // Expected ',' before 'this'
            // auto f = [this] () { // works as expected
                x = 5;
            };
            f();
        }

    private:
        int x;
};

Kafam karışan (ve cevaplanmasını istediğim) tuhaflık, aşağıdakilerin işe yaramasının nedenidir:

auto f = [&] () { /* ... */ }; // capture everything by reference

Ve neden açıkça thisreferansla yakalayamıyorum :

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.

6
Neden olur istemek için? Bir işaretçiye atıfta bulunmanın makul olarak yararlı olabileceği şeyler açısından:this değiştirilemez, bir referansı daha hızlı yapacak kadar büyük değildir ... ve her neyse , aslında mevcut değildir , bu yüzden vardır gerçek bir ömür yok, yani ona yapılan herhangi bir atıf, tanım gereği sallanıyor olacaktı. thisbir değerdir, bir değer değildir.
underscore_d

Yanıtlar:


111

Nedeni [&this]çalışmıyor çünkü bir sözdizimi hatası. İçindeki virgülle ayrılmış her parametre lambda-introducera capture:

capture:
    identifier
    & identifier
    this

Buna &thissözdizimsel olarak izin verilmediğini görebilirsiniz . Buna izin verilmemesinin nedeni this, küçük bir const işaretçisi olduğundan asla referansla yakalamak istememenizdir . Bunu yalnızca değere göre geçirmek isteyeceksiniz - bu nedenle dil this, referansla yakalamayı desteklemiyor .

thisAçıkça yakalamak [this]için lambda-introducer.

İlki captureşu olabilir capture-default:

capture-default:
    &
    =

Bu, kullandığım her şeyi, referansla ( &) veya değere ( =) göre otomatik olarak yakalamak anlamına gelir - ancak bunun işlenmesi thisözeldir - her iki durumda da daha önce verilen nedenlerle değer tarafından yakalanır (varsayılan bir yakalamayla bile &, bu genellikle anlamına gelir) referansla yakalama).

5.1.2.7/8:

Ad arama (3.4) amaçları için, this(9.3.2) ' nin tipini ve değerini belirlemek ve statik olmayan sınıf üyelerine atıfta bulunan id-ifadelerini (*this)(9.3.1) kullanarak sınıf üyesi erişim ifadelerine dönüştürmek , bileşik-ifade [OF THE LAMBDA] lambda ifadesi bağlamında ele alınır.

Bu nedenle, üye adlarını kullanırken (örneğinizde adın kullanımı gibi) lambda, kaplayan üye işlevinin bir parçasıymış gibi davranır x, bu nedenle, thisbir üye işlevinin yaptığı gibi "örtük kullanımlar" üretecektir .

Bir lambda yakalama bir yakalama varsayılanı içeriyorsa, &yani lambda yakalamadaki tanımlayıcılardan önce gelmeyecektir &. Bir lambda yakalama, yakalama varsayılanı içeriyorsa, =lambda yakalama, this ve her bir tanımlayıcıdan önce gelecektir &. Bir tanımlayıcı veya thisbir lambda yakalamada birden fazla görünmemelidir.

Eğer kullanabilirsiniz Yani [this], [&],[=] veya [&,this]bir şekilde lambda-introduceryakalamak için thisdeğerine göre işaretçi.

Ancak [&this]ve [=, this]biçimsizler. Geçen vaka gcc içinde forgivingly için uyarıyor [=,this]o explicit by-copy capture of ‘this’ redundant with by-copy capture defaultziyade hatalar.


3
@KonradRudolph: Bazı şeyleri değere göre ve diğerlerini referans alarak yakalamak isterseniz ne olur? Ya da yakaladığınız şey konusunda çok açık olmak mı istiyorsunuz?
Xeo

2
@KonradRudolph: Bu bir güvenlik özelliği. Yanlışlıkla istemediğiniz isimleri yakalayabilirsiniz.
Andrew Tomazos

8
@KonradRudolph: Blok düzeyindeki yapılar, kullandıkları nesnelere sihirli bir şekilde bir işaretçiyi yeni, görünmez bir anonim türe kopyalamıyor, bu da çevreleyen kapsamda hayatta kalabiliyor - sadece bir ifadede nesnelerin adını kullanarak. Lambda yakalama çok daha tehlikeli bir iştir.
Andrew Tomazos

5
@KonradRudolph " [&]Bir kontrol yapısına geçirilecek bir blok oluşturmak gibi bir şey yapıyorsanız kullanın" derdim , ancak daha az basit amaçlar için kullanılacak bir lambda üretiyorsanız açıkça yakalayın. [&]Lambda mevcut kapsamı aşacaksa korkunç bir fikir. Bununla birlikte, lambdaların birçok kullanımı, blokları kontrol yapılarına geçirmenin yollarıdır ve blok, kapsamda yaratıldığı bloğu aşmaz.
Yakk - Adam Nevraumont

2
@Ruslan: Hayır, thisbir anahtar kelimedir, thistanımlayıcı değildir.
Andrew Tomazos

6

Çünkü standartta &this Yakalama listelerinde :

N4713 8.4.5.2 Yakalar:

lambda-capture:
    capture-default
    capture-list
    capture-default, capture-list

capture-default:
    &
    =
capture-list:
    capture...opt
    capture-list, capture...opt
capture:
    simple-capture
    init-capture
simple-capture:
    identifier
    &identifier
    this
    * this
init-capture:
    identifier initializer
    &identifier initializer
  1. Lambda yakalama amacıyla, bir ifade potansiyel olarak aşağıdaki gibi yerel varlıklara başvurur:

    7.3 A Bu ifade potansiyel olarak buna * atıfta bulunur.

Bu nedenle standart garanti eder thisve *thisgeçerlidir ve &thisgeçersizdir. Ayrıca, yakalama , işaretçiyi değere göre yakalamaktan ziyade , referansla (bir değer olan nesnenin kendisi) yakalama thisanlamına gelir !*thisthis


*thisnesneyi değere
sp2danny
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.