Czy można przekazać funkcję lambda jako wskaźnik funkcji? Jeśli tak, to muszę robić coś niepoprawnie, ponieważ pojawia się błąd kompilacji.
Rozważ następujący przykład
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Kiedy próbuję to skompilować , pojawia się następujący błąd kompilacji:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
To jest jeden komunikat o błędzie do przeczytania, ale myślę, że z tego wyciągam to, że lambda nie może być traktowana jako constexpr
tak, więc nie mogę przekazać go jako wskaźnika funkcji? Próbowałem również stworzyć x
const, ale to nie pomaga.
c++
c++11
lambda
function-pointers
Cory Kramer
źródło
źródło
Odpowiedzi:
Lambda może zostać przekonwertowana na wskaźnik funkcji tylko wtedy, gdy nie jest przechwytywany, ze standardowej sekcji C ++ 11
5.1.2
[expr.prim.lambda] mówi ( wyróżnienie moje ):Zauważ, że cppreference obejmuje to również w swojej sekcji na temat funkcji Lambda .
Więc działałyby następujące alternatywy:
i tak by to:
i jak wskazuje 5gon12eder , możesz także użyć
std::function
, ale pamiętaj, żestd::function
jest to duża waga , więc nie jest to kompromis bez kosztów.źródło
void*
jako jedynego parametru. Zwykle nazywany jest „wskaźnikiem użytkownika”. Jest również stosunkowo lekki, ale zwykle wymagamalloc
trochę miejsca.Odpowiedź Shafik Yaghmour za poprawnie wyjaśnia, dlaczego lambda nie mogą być przekazywane jako wskaźnik funkcji, jeśli ma ona przechwytywanie. Chciałbym pokazać dwie proste poprawki dla problemu.
Użyj
std::function
zamiast surowych wskaźników funkcji.To bardzo czyste rozwiązanie. Należy jednak pamiętać, że zawiera on pewne dodatkowe koszty związane z usuwaniem typu (prawdopodobnie wywołanie funkcji wirtualnej).
Użyj wyrażenia lambda, które niczego nie rejestruje.
Ponieważ Twój predykat jest tak naprawdę stałą logiczną, poniższe elementy szybko obejdą bieżący problem. Zobacz tę odpowiedź, aby uzyskać dobre wyjaśnienie, dlaczego i jak to działa.
źródło
glutReshapeFunc
.std::function
lub lambda - dlaczego by nie miał? Przynajmniej jest to bardziej czytelna składnia. Zwykle należy używać wskaźnika funkcji do interakcji z bibliotekami C (właściwie z dowolną biblioteką zewnętrzną) i upewnić się, że nie można go zmodyfikować, aby zaakceptować std :: function lub lambda.Wyrażenia lambda, nawet te przechwycone, mogą być obsługiwane jako wskaźnik funkcji (wskaźnik do funkcji składowej).
Jest to trudne, ponieważ wyrażenie lambda nie jest prostą funkcją. W rzeczywistości jest to obiekt z operatorem ().
Kiedy jesteś kreatywny, możesz tego użyć! Pomyśl o klasie „function” w stylu std :: function. Jeśli zapiszesz obiekt, możesz również użyć wskaźnika funkcji.
Aby użyć wskaźnika funkcji, możesz użyć:
Aby zbudować klasę, która może zacząć działać jak „std :: function”, najpierw potrzebujesz klasy / struct, niż może przechowywać wskaźnik obiektu i funkcji. Potrzebujesz także operatora (), aby go wykonać:
Dzięki temu możesz teraz uruchamiać przechwycone, nieprzechwycone lambdy, tak jak używasz oryginału:
Ten kod działa z VS2015
Aktualizacja 04.07.17:
źródło
operator()
implementacji, więc jeśli dobrze ją czytam, nie sądzę, że zadziałałoby wywołanie lambda za pomocą wskaźnika funkcji w stylu C , prawda? Właśnie o to pytało pierwotne pytanie.Przechwytywanie lambdas nie może być konwertowane na wskaźniki funkcji, jak wskazano w tej odpowiedzi .
Jednak dostarczenie wskaźnika funkcji do interfejsu API, który akceptuje tylko jeden, jest często dość uciążliwe. Najczęściej cytowaną metodą jest zapewnienie funkcji i wywołanie z nią obiektu statycznego.
To jest nudne. Kontynuujemy ten pomysł i automatyzujemy proces tworzenia
wrapper
i ułatwiamy życie.I użyj go jako
Relacja na żywo
Zasadniczo deklaruje to anonimową funkcję przy każdym wystąpieniu
fnptr
.Zauważ, że wywołania
fnptr
nadpisują wcześniej napisanecallable
podane kalendarze tego samego typu. W pewnym stopniu naprawiamy to za pomocąint
parametruN
.źródło
Skrót do używania lambda ze wskaźnikiem funkcji C jest następujący:
Używanie Curl jako przykładu ( informacje o debugowaniu curl )
źródło
+
sztuczka).Chociaż podejście wzorcowe jest sprytne z różnych powodów, ważne jest, aby pamiętać o cyklu życia lambda i zarejestrowanych zmiennych. Jeśli ma być użyta jakakolwiek forma wskaźnika lambda, a lambda nie jest kontynuacją w dół, wówczas należy użyć tylko kopiującej [=] lambda. To znaczy, nawet wtedy przechwytywanie wskaźnika do zmiennej na stosie jest NIEBEZPIECZNE, jeśli czas życia tych przechwyconych wskaźników (odwijanie stosu) jest krótszy niż okres eksploatacji lambda.
Prostszym rozwiązaniem do uchwycenia lambda jako wskaźnika jest:
na przykład,
new std::function<void()>([=]() -> void {...}
Pamiętaj tylko, aby później
delete pLamdba
, aby nie dopuścić do wycieku pamięci lambda. Tajemnicą do zrozumienia tutaj jest to, że lambdas mogą przechwytywać lambdas (zadaj sobie pytanie, jak to działa), a także, żestd::function
aby działać ogólnie, implementacja lambda musi zawierać wystarczające informacje wewnętrzne, aby zapewnić dostęp do wielkości lambda (i przechwyconych) danych ( dlategodelete
powinien działać [uruchamianie niszczycieli przechwyconych typów]).źródło
new
głowę funkcją - std :: już zapisuje lambda na stercie ORAZ unika konieczności pamiętania wywołania delete.Nie bezpośrednia odpowiedź, ale niewielka odmiana użycia wzorca szablonu „funktora”, aby ukryć specyfikę typu lambda i zachować ładny i prosty kod.
Nie byłem pewien, jak chcesz użyć klasy decyzyjnej, więc musiałem rozszerzyć klasę o funkcję, która z niej korzysta. Zobacz pełny przykład tutaj: https://godbolt.org/z/jtByqE
Podstawowa forma twojej klasy może wyglądać następująco:
Gdzie przekazujesz typ funkcji jako część zastosowanego typu klasy:
Ponownie nie byłem pewien, dlaczego to robisz
x
, bardziej sensowne (dla mnie) było posiadanie parametru przekazywanego do lambda), abyś mógł użyć:Zobacz link do pełnego przykładu
źródło
Jak wspomnieli inni, możesz zastąpić funkcję Lambda zamiast wskaźnika funkcji. Używam tej metody w moim interfejsie C ++ do FKS ODS solver RKSUITE.
źródło