Sabit referans olarak Lambda yakalama?


166

Lambda ifadesinde const referansı ile yakalamak mümkün müdür?

Aşağıda işaretlenen atamanın başarısız olmasını istiyorum, örneğin:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

Güncelleme: Bu eski bir soru olduğundan, C ++ 14'te bu konuda yardımcı olacak tesisler varsa güncellemek iyi olabilir. C ++ 14'teki uzantılar const referansı ile const olmayan bir nesneyi yakalamamıza izin veriyor mu? ( Ağustos 2015 )


: gibi lambda görünmemeli [&, &best_string](string const s) { ...}?
erjot

3
gerçekten tutarsız yakalama. "const &", lambda işlevinde erişilmesi gereken ancak değiştirilmemesi gereken büyük const nesneniz olduğunda çok yararlı olabilir
sergtk

koda bakıyorum. iki parametreli bir lambda kullanabilir ve ikincisini sabit referans olarak bağlayabilirsiniz. olsa bir maliyet ile geliyor.
Alex

1
C ++ 11'de bu mümkün görünmüyor. Ama belki bu soruyu C ++ 14 için güncelleyebiliriz - buna izin veren uzantılar var mı? C ++ 14 genelleştirilmiş lambda yakalar mı?
Aaron McDaid

Yanıtlar:


127

const n3092'den itibaren yakalamaların gramerinde değil:

capture:
  identifier
  & identifier
  this

Metin yalnızca kopyala yakalama ve referansla yakalamadan bahseder ve herhangi bir sabitlikten bahsetmez.

Bana bir gözetim gibi geliyor, ama standardizasyon sürecini çok yakından takip etmedim.


47
Ben sadece bir değişebilir mutable, ama olması gereken yakalama değiştirilmiş bir değişken geri izledi const. Ya da daha doğrusu, eğer yakalama değişkeni constolsaydı, derleyici programcı üzerinde doğru davranışı uygulardı. Sözdizimi desteklenirse iyi olur [&mutableVar, const &constVar].
Sean

Bu C ++ 14 ile mümkün olması gibi görünüyor, ama işe alamıyorum. Baska öneri?
Aaron McDaid

38
Sabitlik yakalanan değişkenden miras alınır. Eğer yakalama istediğiniz Yani eğer aolarak const, beyan const auto &b = a;lambda ve yakalama önceb
StenSoft

7
@StenSoft Bleargh. Görünüşe göre bu, üye değişkenini referans olarak yakalarken geçerli değildir: [&foo = this->foo]bir constfonksiyonun içinde , yakalamanın kendisinin niteleyicileri attığını belirten bir hata veriyor . Sanırım, bu GCC 5.1'de bir hata olabilir.
Kyle Strand

119

İçinde static_cast/ kullanarak const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO


İçinde kullanarak std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO 2


Ayrıca, belki de bu kabul edilen cevapta düzenlenmelidir? Her iki durumda da, hem c ++ 11 hem de c ++ 14'ü kapsayan iyi bir yanıt olmalıdır. Her ne kadar, sanırım c ++ 14'ün önümüzdeki yıllarda herkes için yeterince iyi olacağı iddia edilebilir
Aaron McDaid

12
@AaronMcDaid const_cast, geçici bir nesneyi sabit bir nesneye koşulsuz olarak değiştirebilir (yayınlanması istendiğinde const), bu nedenle, tercih ettiğim kısıtlamaları eklemek içinstatic_cast
Piotr Skotnicki

1
Diğer taraftan @PiotrSkotnicki, static_castconst referansı, türü tam olarak doğru alamadıysanız sessizce geçici bir süre oluşturabilir
MM

24
@MM &basic_string = std::as_const(best_string)tüm sorunları
çözmeli

15
Bu yazma şeye çirkin bir yolu olma sorunu dışında @PiotrSkotnicki gerektiği kadar basit gibi olacaktır const& best_string.
Kyle Strand

13

Bence yakalama kısmı const, yakalama aracı olarak sadece dış kapsam değişkenine erişmenin bir yolunu belirtmemelidir.

Belirtici dış kapsamda daha iyi belirtilir.

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

lambda işlevi const (kapsamındaki değeri değiştiremez), bu nedenle değişkeni değere göre yakaladığınızda, değişken değiştirilemez, ancak başvuru lambda kapsamında değildir.


1
@Amarnath Balasubramani: Bu sadece benim düşüncem, bence lambda yakalama kısmında bir const referansı belirtmeye gerek yok, neden burada değişken bir const olmalı ve başka bir yerde const olmamalı (eğer mümkünse hataya eğilimli olacak) ). yine de cevabını gördüğüme sevindim.
zhb

2
better_stringKapsayıcı kapsam içinde değişiklik yapmanız gerekiyorsa , bu çözüm çalışmaz. Const-ref olarak yakalama için kullanım durumu, değişkenin kapsayıcı kapsamda değiştirilebilir olması, ancak lambda içinde olmaması gerektiğidir.
Jonathan Sharman

@JonathanSharman, bir değişkene const referansı oluşturmak için hiçbir ücrete tabi değildir, bu yüzden bunu yapabilir const string &c_better_string = better_string;ve mutlu bir şekilde lambda'ya iletebilirsiniz:[&c_better_string]
Steed

@Steed Sorun, çevredeki kapsama fazladan bir değişken adı eklemenizdir. Bence Piotr Skotnicki'nin yukarıdaki çözümü en temiz çözümdür, çünkü değişken kapsamları minimum düzeyde tutarken sabit doğruluk sağlar.
Jonathan Sharman

@JonathanSharman, burada görüş diyarına giriyoruz - en güzel, en temiz ya da her neyse. Demek istediğim, her iki çözümün de göreve uygun olmasıdır.
Steed

8

Değişkeni functor parametresi olarak kullanmıyorsanız, o zaman geçerli işlevin erişim düzeyini kullanmalısınız. Yapmamanız gerektiğini düşünüyorsanız, lambda'nızı bu işlevden ayırın, bunun bir parçası değildir.

Her neyse, bunun yerine başka bir const referansı kullanarak istediğiniz şeyi kolayca elde edebilirsiniz:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

Ancak bu, lambda'nızın mevcut işlevden izole edilmesi gerektiğini varsaymakla aynıdır ve lambda olmayan bir hale getirir.


1
Yakalama maddesinde hala best_stringsadece bahsediliyor . Bunun dışında, GCC 4.5 amaçlanan kodu başarılı bir şekilde reddeder.
sellibitze

Evet, bu bana teknik düzeyde elde etmeye çalıştığım sonuçları verecektir. Nihayetinde, asıl sorumun cevabı "hayır" gibi görünüyor.
John Dibling

Neden bunu bir lambda değil?

Çünkü lambda'nın doğası, bağlama bağlı olmasıdır. Belirli bir bağlama ihtiyacınız yoksa, işlev oluşturmanın hızlı bir yoludur. Functor bağlamdan bağımsız olmalıdırsa, gerçek bir functor yapın.
Klaim

3
"Eğer functor bağlamdan bağımsız olmalı, onu gerçek bir functor yap" ... ve olası bir veda öpücüğü mümkün mü?
Andrew Lazarus

5

Üç farklı seçeneğiniz olduğunu düşünüyorum:

  • const başvurusu kullanmayın, ancak kopya yakalama kullanın
  • değiştirilebilir olduğu gerçeğini görmezden gel
  • consd referansı olan bir ikili işlevin bir argümanını bağlamak için std :: bind kullanın.

bir kopyasını kullanma

Kopya yakalamalı lambdalarla ilgili ilginç kısım, bunların aslında salt okunur olması ve bu nedenle tam olarak ne yapmak istediğinizi yapmasıdır.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

std :: bind kullanarak

std::bindbir fonksiyonun bütünlüğünü azaltır. Ancak bunun bir işlev işaretçisi aracılığıyla dolaylı bir işlev çağrısına yol açabileceğini / bulacağını unutmayın.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

1
Kapsayıcı kapsamdaki değişkente yapılan değişiklikler dışında lambda'ya yansıtılmayacaktır. Bu bir referans değil, sadece yeniden atanmaması gereken bir değişkendir çünkü yeniden atama ne anlama geldiği anlamına gelmez.
Grault


0

Clang kullanın veya bu gcc hatası giderilene kadar bekleyin: hata 70385: const referansı referansı ile Lambda yakalama başarısız oluyor [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]


1
Bu bağlantı soruyu cevaplayabilse de, cevabın temel kısımlarını buraya eklemek ve bağlantıyı referans olarak sağlamak daha iyidir. Bağlantı verilen sayfa değişirse yalnızca bağlantı yanıtları geçersiz olabilir. ”
Div

Tamam, cevabımı gcc hata açıklaması eklemek için düzenledim.
user1448926

Bu, eğer varsa, soruya dolaylı bir cevaptır. Hata, bir derleyicinin bir şeyi yakalarken nasıl başarısız olduğu ile ilgilidir, bu yüzden belki de sorunun nedenini ele almanın veya çözümün bir yolu gcc ile çalışmayabilir.
Stein

0

Bir const kullanmak sadece algoritma ampers'ına sahip olacak ve dizeyi orijinal değerine ayarlayacaktır. Bununla birlikte, dizeyi tipik [&, & best_string] (string const s) olarak tanımlamaz. Bu nedenle , referansı yakalamaya çalışırken onu bırakmamız daha olasıdır.


Bu çok eski bir soru: Cevabınız hangi C ++ sürümüne atıfta bulunduğunuzla ilgili bağlamda eksik. Lütfen bu içeriği sağlayın.
ZF007
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.