2012-06-12 25 views
8

Zaczynam tworzyć aplikacje wykorzystujące lambdy C++ 11 i muszę przekonwertować niektóre typy na wskaźniki funkcji. Działa to doskonale w GCC 4.6.0:C++ 11 lambdas do wskaźnika funkcji

void (* test)() = []() 
{ 
    puts("Test!"); 
}; 

test(); 

mój problem jest, gdy trzeba użyć funkcji lub metody zmiennych lokalnych wewnątrz lambda:

const char * text = "test!"; 

void (* test)() = [&]() 
{ 
    puts(text); 
}; 

test(); 

g ++ 4.6.0 daje kod błędu obsada:

main.cpp: In function 'void init(int)': 
main.cpp:10:2: error: cannot convert 'main(int argc, char ** argv)::<lambda()>' to 'void (*)()' in initialization 

Jeśli użycie auto, to działa ok:

const char * text = "Test!"; 

auto test = [&]() 
{ 
    puts(text); 
}; 

test(); 

Moje pytanie brzmi: jak utworzyć typ dla lambda z [&]? W moim przypadku nie mogę użyć STL std :: function (ponieważ mój program nie używa C++ RTTI i EXCEPTIONS runtime) i ma prostą implementację funkcji, aby rozwiązać ten problem?

+8

Brzmi jak instancja [problemu XY] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). –

+3

Nie można przekonwertować przechwytywanej wartości lambda na wskaźnik funkcji. Wyjaśnij, co próbujesz zrobić. –

+0

Wyjaśniłeś (źle) próbę rozwiązania, nie opisując jednak faktycznego problemu. Powiedz nam, jaki masz prawdziwy problem: – thecoshman

Odpowiedz

8

nie mogę korzystać z STL std :: function (ponieważ mój program nie używa C++ Runtime RTTI i EXCEPTIONS)

Następnie możesz potrzebować napisać własny odpowiednik std::function.

Typowa implementacja usuwania typu dla std::function nie wymaga RTTI dla większości jego funkcji; działa poprzez regularne wywoływanie funkcji wirtualnych. Więc pisanie własnej wersji jest wykonalne.

Rzeczywiście, tylko rzeczy w std::function że potrzebne RTTI są target_type i target funkcje, które nie są najbardziej przydatne funkcje w świecie. Być może będziesz mógł po prostu użyć std::function bez wywoływania tych funkcji, zakładając, że używana implementacja nie wymaga RTTI dla zwykłej działalności.

Zwykle po wyłączeniu obsługi wyjątków program po prostu się wyłącza i wyświetla błędy po napotkaniu instrukcji throw.A ponieważ większość wyjątków, które wyemitowałby std::function, nie jest rzeczą, z której można by odzyskać (wywołanie pustego function, wyczerpanie pamięci, itp.), Prawdopodobnie można po prostu użyć std::function jak jest.

8

Tylko lambdas bez przechwytywania można przekonwertować na wskaźnik funkcji. Jest to rozszerzenie lambdas tylko dla tego konkretnego przypadku [*]. Ogólnie rzecz biorąc, lambdy są obiektami funkcyjnymi i nie można konwertować obiektu funkcji na funkcję.

Alternatywą dla lambd, które mają stan (przechwytywanie), jest użycie std::function zamiast zwykłego wskaźnika funkcji.


[*]: Jeśli lambda, która ma stan, może zostać przekonwertowana na wskaźnik funkcyjny, to w jaki sposób będzie utrzymywany stan? (Zauważ, że może istnieć wiele wystąpień tego konkretnego lambda, każdy z jego własnego państwa, które muszą być utrzymywane oddzielnie)

+0

Cóż, jeśli przekazać go przez wartość taką jak ta 'const int c; [c] (int i) -> int {return i * c;} 'to by miało dużo sensu ... –

+0

@BarnabasSzabolcs: To zależy, rozważ' std :: function f (int x) {const int c = x; return [c] (int i) {return i * c;}}; '. Funktor zwrócony przez 'f' zależy od jego argumentu, a zatem ma stan. Tylko wtedy, gdy przechwytywanie było wyrażeniem stałym, można je uwzględnić w zwykłej funkcji. –

+0

Hm. Zgadzam się, częściowo. Nadal można go wyciągnąć, ale rozwiązanie może być kosztowne, ponieważ program musiałby za każdym razem skopiować kod funkcji lambda, zmieniając wartość c w kodzie funkcji. (lub jeśli nie kopiuje całego kodu funkcji, musi wykonać zaplanowane skoki, w których skoki mogą spowolnić proces.) –

6

Jak już wspomniano, tylko lambdy, które niczego nie przechwytują, można przekonwertować na wskaźniki funkcji.

Jeśli nie chcesz używać ani pisać czegoś w stylu std :: function, inną alternatywą jest przekazanie jako parametrów rzeczy, które w przeciwnym razie zostałyby przechwycone. Możesz nawet utworzyć strukturę do ich przechowywania.

#include <iostream> 

struct captures { int x; }; 
int (*func)(captures *c) = [](captures *c){ return c->x; }; 

int main() { 
    captures c = {10}; 

    std::cout << func(&c) << '\n'; 
} 

Inną alternatywą jest wykorzystanie globalnych/statyczne/thread_local/constexpr zmiennych, które nie wymagają przechwytywanie.

Powiązane problemy