2014-04-06 12 views
7

Właśnie zacząłem uczyć się nowych funkcji w C++ 11. Czytałem o lambdach w C++ Primer (Stanley Lippman) i eksperymentowałem z nimi.Wyrażenia lambda C++ - w jaki sposób kompilator je interpretuje?

Próbowałem następujące fragmenty kodu:

auto func() -> int (*)(){ 
    //int c=0; 
    return []()-> int {return 0;}; 
} 

int main(){ 
    auto p = func(); 
} 

ten kod skompilowany w porządku. Sądzę więc, że lambdy bez jakichkolwiek przechwytów są generowane przez kompilator jako zwykłe funkcje i możemy użyć do nich normalnego wskaźnika funkcji.

Teraz zmieniłem kod używać przechwytuje:

auto func() -> int (*)(){ 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

int main(){ 
    auto p = func(); 
} 

Ale to nie udało się skompilować. Mam następujący błąd kompilacji podczas korzystania g ++:

main.cpp: In function ‘int (* func())()’: 
main.cpp:6:31: error: cannot convert ‘func()::__lambda0’ to ‘int (*)()’ in return 
return [=]()-> int {return c;}; 

Z błędu mogę zrozumieć, że nie jest to normalna funkcja, która jest generowana i może prawdopodobnie być klasą z przeciążonej call-operatora. A może to coś innego?

Moje pytania: W jaki sposób kompilator obsługuje wewnętrznie lambdy? W jaki sposób powinienem przekazać informacje o lambdach, które używają przechwytujących, tj. Jaka powinna być wartość zwracana przez func()? Nie mogę obecnie myśleć o przypadku użycia, w którym musiałbym używać takich lambdas, ale chcę po prostu lepiej o nich wiedzieć. Proszę pomóż.

Dzięki.

+0

Try ten link: http://www.cprogramming.com/c++11/c++11-lambda-closures.html –

Odpowiedz

8

Wszystkie lambdas są funkcją obiektów z realizacji określono typ o nazwie typ zamknięcia z członkiem operator(). Każde wyrażenie lambda ma również własny unikalny typ zamknięcia.

Lambda bez przechwytywania może być przekształcona w wskaźnik funkcji. To, czy kompilator generuje normalną funkcję za kulisami, jest wewnętrznym szczegółem i nie powinno mieć dla ciebie znaczenia.

Nie można zwrócić wartości lambda zdefiniowanej w funkcji. Jest kilka rzeczy, które temu zapobiegają - nie znasz nazwy typu wyrażenia lambda, nie możesz użyć wyrażenia lambda wewnątrz decltype i jak już wspomniano, dwa wyrażenia lambda (nawet jeśli są identyczne pod względem leksykalnym) mają różne typy .

Co możesz zrobić, to użyć std::function:

std::function<int()> func() 
{ 
    int i = 0; 
    return [=]()-> int {return i;}; 
} 

ten sposób współpracuje ze zrzutów, too.

Albo coś takiego:

auto f = []{ return 0; }; 

auto func() -> decltype(f) 
{ 
    return f; 
} 

EDIT: Standard C++ zbliżający 1r (dokładniej typ zwracany odliczenia), ale pozwoli Ci to zrobić:

auto func() 
{ 
    int i = 42; 
    return [=]{ return i; }; 
} 
+2

Jedyna odpowiedź, aby wspomnieć o konwersji na wskaźnik funkcji! ?? +1 Zwróć uwagę, że możesz zwrócić lambdę z lambda, aw C++ 1y również z funkcji, poprzez dedukcję typu powrotu. Typ wyrażenia lambda jest * nigdy * typem funkcji dla dowolnego obserwowalnego zachowania (oczywiście implementuje się go zazwyczaj jako funkcję (członka)). Na przykład, jest zagwarantowane, że lambda nie będzie pasowała do jakiegoś generycznego 'Ret (Params ...)' w dedukcji typu szablonu. – dyp

+0

@dyp Prawo, dzięki. Zaktualizowałem przykład odliczenia zwrotu. – jrok

2

Kompilator może zdecydować, jak wygenerować typ funkcji lambda. Generuje obiekt funkcji, co oznacza, że ​​możesz go wywołać przez (). Ale typ może się różnić w różnych sytuacjach. Każda puszka lambda ma unikalny typ. Prosta lambda (nie przechwytuje niczego) może być zapisana w wskaźniku do funkcji.

w C++ 11, zawinąć lambda, który przechwytuje dane, można użyć std::function:

auto func() -> std::function<int()> { 
    int c = 0; 
    return [=]()-> int {return c;}; 
} 

 

w C++ 14, ma function return type deduction, niech kompilacji poradzić :

auto func() { 
    int c = 0; 
    return [=]()-> int {return c;}; 
} 
+0

Nie przechwytujące lambdy nie mogą być przechowywane w wskaźniku do funkcji: można je konwertować na wskaźnik funkcjonować. – Yakk

+0

@Yakk: Czy możesz wyjaśnić różnicę między 'be stored' i' be convert'? Jeśli masz na myśli, że nie przechwytujące lambdy zostaną rzucone przed zapisaniem do wskaźnika, to jesteś w błędzie. Lambdas nie ma określonych typów, kto wie, może kompilator używa wskaźnika do typu funkcji dla prostych lambd i mogą być bezpośrednio przypisane do wskaźnika do funkcji. – deepmax

+0

Wartość lambda ** nie ** pasuje do wskaźnika funkcji "template", gwarantowana. Więc nie, nie wierzę, że masz rację. W praktyce każdy kompilator używa wygenerowanego unikalnego typu, który sprawia, że ​​inlinimg wywołania trywialnego: wskaźnik funkcji sprawia, że ​​inlinimg jest trudny i kruchy. Tak więc w teorii iw praktyce jest to konwersja do innego typu o różnych właściwościach. – Yakk

2

Funkcja nie może zwrócić samej funkcji lambda. Jeśli piszesz:

auto func() -> int() { 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

Następnie GCC narzeka:

lambda.cpp:3:21: error: function return type cannot be function 
auto func() -> int(){ 

Więc rozwiązaniem jest owinąć obiekt powrotną do std::function:

#include <functional> 
    auto func() -> std::function<int()> { 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

int main(){ 
    auto p = func(); 
}