2011-01-18 29 views
61

Zastanawiam się, czy można napisać funkcję, która zwraca funkcję lambda w C++ 11. Oczywiście jednym z problemów jest deklaracja takiej funkcji. Każda lambda ma typ, ale tego typu nie można wyrazić w C++. Nie sądzę, że to będzie działać:Funkcja zwracająca wyrażenie lambda

auto retFun() -> decltype ([](int x) -> int) 
{ 
    return [](int x) { return x; } 
} 

Nor to:

int(int) retFun(); 

nie jestem świadomy żadnej automatycznej konwersji z lambda do, powiedzmy, wskaźniki do funkcji, czy coś takiego. Czy jedynym rozwiązaniem jest ręczne opracowanie obiektu funkcji i zwrócenie go?

+0

Aby dodać to, co zostało już powiedziane, bezpaństwowe funkcje lambda można zamienić na wskaźniki funkcji. –

+2

IMO twoja pierwsza opcja nie będzie działać, ponieważ lambda w 'decltype' nie jest taka sama jak w ciele funkcji i dlatego ma inny typ (nawet jeśli zawarłeś zwrotną instrukcję) – Motti

+1

Przy okazji, jeśli lambda ma pustą klauzulę przechwytywania, może być niejawnie przekształcalna na wskaźnik do funkcji. – GManNickG

Odpowiedz

70

Nie trzeba się rzemiosła obiekt funkcji, wystarczy użyć std::function, do którego funkcje lambda są wymienialne:

std::function<int (int)> retFun() { 
    return [](int x) { return x; }; 
} 
+4

Duh! Uwielbiam StackOverflow. Badanie tematu zajęłoby mi dużo czasu, a następnie uzyska odpowiedź na temat StackOverflow. Dzięki Sean! –

+3

To spowoduje alokację pamięci w konstruktorze 'std :: function'. –

+2

@Maxim Yegorushkin std :: function ma semantykę ruchu plus może używać niestandardowych alokatorów, a wersja robocza C++ 0x ma następujące uwagi: "[Uwaga: zachęty do implementacji unikają użycia dynamicznie przydzielanej pamięci dla małych obiektów wywoływanych, dla przykład, gdzie celem f jest obiekt zawierający tylko wskaźnik lub odniesienie do obiektu i wskaźnik funkcji składowej. -end note] "więc zasadniczo nie można przyjąć wielu założeń dotyczących strategii alokacji, której używa konkretna implementacja, ale powinieneś być w stanie wykorzystać własne (połączone) alokatory. –

27

Na tym prostym przykładzie, nie trzeba std::function.

Od standardowego §5.1.2/6:

Rodzaj zamknięcia dla lambda wyrażenia bez lambda-wychwytywania ma publicznego non-virtual bez wyraźnej const funkcję konwersji do wskaźnika do funkcji o tym samym parametrze i typach zwracanych, co operator wywołania funkcji typu zamknięcia. Wartość zwrócona przez tę funkcję konwersji jest adresem funkcji, która po wywołaniu ma taki sam skutek jak wywołanie operatora wywołania funkcji typu zamknięcia.

Ponieważ funkcja nie posiada wychwytywania, to znaczy, że lambda można przekształcić do wskaźnika do funkcji typu int (*)(int):

typedef int (*identity_t)(int); // works with gcc 
identity_t retFun() { 
    return [](int x) { return x; }; 
} 

To moja zrozumienie, poprawcie mnie jeśli jestem źle.

+1

To brzmi dobrze. Niestety nie działa z bieżącym kompilatorem, którego używam: VS 2010. Std :: funkcja konwersji działa. –

+3

Tak, ostateczne sformułowanie tej zasady nastąpiło zbyt późno dla VC2010. –

+0

Dodałem przykład kodu. Oto [pełny program] (https://gist.github.com/1342212). – jfs

12

Możesz zwrócić funkcję lambda z innej funkcji lambda, ponieważ nie powinieneś jawnie określać typu powrotu funkcji lambda. Wystarczy napisać coś takiego w zakresie globalnym:

auto retFun = []() { 
    return [](int x) {return x;}; 
}; 
+2

Jest to prawdziwe tylko wtedy, gdy zewnętrzna lambda składa się tylko z instrukcji return. W przeciwnym razie musisz określić typ zwrotu. –

+1

To jest najlepsza odpowiedź, ponieważ nie wymaga polimorfizmu runtime funkcji std :: i pozwala lambda mieć niepustą listę przechwytywania, jednak użyłbym ** const ** auto fun = ... –

10

Choć pytanie konkretnie prosi o C++ 11, przez wzgląd na innych, którzy natknąć to i mają dostęp do kompilatora C++ 14, C++ 14 umożliwia teraz wyprowadzone typy zwrotu dla zwykłych funkcji. Więc przykład w pytaniu można regulować tak, aby działać zgodnie z oczekiwaniami po prostu przez upuszczenie -> decltype ... klauzuli po liście parametrów funkcji:

auto retFun() 
{ 
    return [](int x) { return x; } 
} 

jednak pamiętać, że to nie będzie działać, jeśli więcej niż jeden return <lambda>; pojawia się w funkcji. Dzieje się tak dlatego, że ograniczenie dotyczące dedukcji typu zwracanego jest takie, że wszystkie instrukcje return muszą zwracać wyrażenia tego samego typu, ale każdy obiekt lambda ma swój własny unikalny typ przez kompilator, więc wyrażenia return <lambda>; będą miały inny typ.

+2

. wyprowadzone typy C++ 14, ale pomijają polimorficzne lambdy? 'auto retFun() {return [] (auto const & x) {return x; }; } ' – sehe