Czy możliwe jest przechwycenie przez odwołanie do stałej w wyrażeniu lambda?
Chcę, aby zadanie zaznaczone poniżej zakończyło się niepowodzeniem, na przykład:
#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;
}
Aktualizacja: ponieważ jest to stare pytanie, warto je zaktualizować, jeśli w C ++ 14 są narzędzia, które mogą w tym pomóc. Czy rozszerzenia w C ++ 14 pozwalają nam przechwytywać obiekt inny niż const za pomocą odwołania do stałej? ( Sierpień 2015 )
[&, &best_string](string const s) { ...}
:?Odpowiedzi:
const
nie ma w gramatyce przechwytywania od n3092:Tekst wspomina tylko o przechwytywaniu przez kopię i przechwytywaniu przez odniesienie i nie wspomina o żadnym rodzaju trwałości.
Wydaje mi się, że to przeoczenie, ale nie śledziłem zbyt dokładnie procesu normalizacji.
źródło
const
. Albo bardziej poprawnie, gdyby zmienną przechwytywania byłaconst
, kompilator wymusiłby prawidłowe zachowanie programisty. Byłoby miło, gdyby składnia była obsługiwana[&mutableVar, const &constVar]
.a
jakoconst
, zadeklarujconst auto &b = a;
przedb
[&foo = this->foo]
wewnątrzconst
funkcji pojawia się błąd informujący, że przechwytywanie samo odrzuca kwalifikatory. Przypuszczam, że może to być błąd w GCC 5.1.W c ++ 14używając
static_cast
/const_cast
:PRÓBNY
W c ++ 17używając
std::as_const
:DEMO 2
źródło
const_cast
może bezwarunkowo zmienić zmienny obiekt na obiekt const (gdy zostanie poproszony o rzutowanieconst
), a więc do dodawania ograniczeń, które preferujęstatic_cast
static_cast
odwołanie do const może po cichu stworzyć tymczasowy, jeśli nie masz dokładnie tego typu&basic_string = std::as_const(best_string)
powinien rozwiązać wszystkie problemyconst& best_string
.Myślę, że część przechwytywania nie powinna określać
const
, ponieważ przechwytywanie oznacza, że potrzebuje tylko sposobu na dostęp do zmiennej zakresu zewnętrznego.Specyfikator jest lepiej określony w zakresie zewnętrznym.
Funkcja lambda jest stała (nie można zmienić wartości w swoim zakresie), więc kiedy przechwytujesz zmienną według wartości, zmienna nie może zostać zmieniona, ale odwołanie nie znajduje się w zakresie lambda.
źródło
better_string
w zakresie zawierającym, to rozwiązanie nie zadziała. Przykład użycia do przechwytywania jako const-ref ma miejsce, gdy zmienna musi być modyfikowalna w zakresie zawierającym, ale nie w zakresie lambda.const string &c_better_string = better_string;
i szczęśliwie przekazać ją do lambdy:[&c_better_string]
Wydaje mi się, że jeśli nie używasz zmiennej jako parametru funktora, powinieneś użyć poziomu dostępu bieżącej funkcji. Jeśli uważasz, że nie powinieneś, oddziel swoją lambdę od tej funkcji, nie jest jej częścią.
W każdym razie możesz łatwo osiągnąć to samo, co chcesz, używając zamiast tego innego odwołania do stałej:
Ale to to samo, co założenie, że twoja lambda musi być odizolowana od bieżącej funkcji, co czyni ją inną niż lambda.
źródło
best_string
tylko. Poza tym GCC 4.5 „skutecznie odrzuca” kod zgodnie z zamierzeniami.Myślę, że masz trzy różne opcje:
za pomocą kopii
Ciekawą częścią lambd z przechwytywaniem kopii jest to, że są one w rzeczywistości tylko do odczytu i dlatego robią dokładnie to, czego chcesz.
używając std :: bind
std::bind
zmniejsza liczbę funkcji. Należy jednak zauważyć, że może to / doprowadzi do pośredniego wywołania funkcji przez wskaźnik funkcji.źródło
Jest krótszy sposób.
Zauważ, że przed „best_string” nie ma znaku ampersand.
Będzie to typ „const std :: reference_wrapper << T >>”.
http://coliru.stacked-crooked.com/a/0e54d6f9441e6867
źródło
Użyj clang lub poczekaj, aż ten błąd gcc zostanie naprawiony: błąd 70385: przechwytywanie Lambdy przez odniesienie do const nie powiedzie się [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]
źródło
Użycie stałej spowoduje po prostu, że algorytm ampersand ustawi ciąg na jego oryginalną wartość, innymi słowy, lambda nie będzie tak naprawdę definiować siebie jako parametru funkcji, chociaż otaczający zakres będzie miał dodatkową zmienną ... Bez jej definiowania chociaż nie zdefiniowałaby ciągu jako typowego [&, & best_string] (string const s) Dlatego najprawdopodobniej lepiej będzie, jeśli po prostu zostawimy go na tym, próbując przechwycić odniesienie.
źródło