wersji podstawowej, do stosowania w pliku nagłówka:
template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
Bardziej skomplikowana wersja, jeśli chcesz podzielić interfejsu od implementacji (to prowadzi koszty czas):
bool Func1(int Arg1, std::function<bool(int)> Arg2){
if(Arg1 > 0){
return Arg2(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
std::function
używa typu skasowaniu stworzyć niestandardowym owijkę wokół lambda i następnie wyświetla interfejs inny niż wirtualny, który używa wzorca pImpl
, aby przekazać go do niestandardowego opakowania.
Lub, w warunkach mniej technicznych, std::function<bool(int)>
jest klasa, które można zawinąć prawie wszystko, co można nazwać jak funkcja, przekazując jeden parametr, który jest kompatybilny z przepuszczanie int
i zwraca coś, co jest zgodne z zwracając numer bool
.
Rozmowa przez std::function
ma koszt czasu prowadzony w przybliżeniu równą rozmowy virtual
funkcyjnego (spowodowane przez wyżej typu skasowaniem), a podczas jego tworzenia musi skopiować stan obiektu funkcyjnego (aka funktora) upłynął w (które mogą być tanie - bezpaństwowe lambdas lub lambdas przechwytujące argumenty przez odniesienie - lub drogie w niektórych innych przypadkach) i przechowujące je (zazwyczaj w wolnym sklepie lub kupie, który ma koszt), podczas gdy wersje czysto szablonowe może być "podkreślony" w momencie wywołania (tj. może nie tylko kosztować mniej niż wywołanie funkcji, kompilator może nawet zoptymalizować ponad granicami wywołania funkcji i powrotu!)
Fantazyjna wersja pierwszego przykładu, który również lepiej radzi sobie z niektórymi przypadkami narożnymi: (również musi być i mplemented w pliku nagłówka, lub w tej samej jednostce tłumaczeniowej, ponieważ jest wykorzystywany)
template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
if(Arg1 > 0){
return std::forward<Lambda>(Arg2)(Arg1);
} else {
return false; // remember, all control paths must return a value
}
}
który wykorzystuje technikę znaną jako „doskonały” spedycji.Dla niektórych funktorów generuje to nieco inne zachowanie niż # 1 (i zazwyczaj bardziej poprawne zachowanie).
Większość poprawy pochodzi tworzą użycie &&
w liście argumentów: oznacza to, że odniesienie do funktora jest przekazywana (zamiast kopii), oszczędzając pewne koszty i pozwala zarówno const
lub nie const
funktor być przekazywane w.
zmiana std::forward<Lambda>(...)
spowodowałoby jedynie zmianę w zachowaniu, czy ktoś używał stosunkowo nową funkcję C++, który umożliwia metod (w tym operator()
), aby zastąpić na rvalue/lwartości status wskaźnika this
. Teoretycznie może to być przydatne, ale liczba funktorów, które widziałem, które faktycznie przesłoniły w oparciu o stan rvalue this
jest 0
. Kiedy piszę poważny kod biblioteczny (tm) idę na to, ale rzadko inaczej.
Jest jeszcze jedna możliwa rzecz do rozważenia. Załóżmy, że chcesz wziąć albo funkcję, która zwraca bool
, albo funkcję, która zwraca void
, i jeśli funkcja zwraca void
chcesz traktować ją tak, jakby wróciła true
. Jako przykład bierzesz funkcję, która jest wywoływana podczas iteracji nad pewną kolekcją, i chcesz opcjonalnie wspomóc wczesne zatrzymanie. Funkcja zwraca false
, gdy chce się zatrzymać przedwcześnie, i true
lub void
w przeciwnym razie.
Albo, w bardziej ogólnym przypadku, jeśli masz kilka nadpisań funkcji, z których jedna przyjmuje funkcję, a inne przyjmują inny typ w tym samym miejscu.
Jest to możliwe, o ile się tu dostanę (za pomocą inteligentnego adaptera lub za pomocą technik SFINAE). Jednak prawdopodobnie lepiej będzie po prostu utworzyć dwie różne funkcje, ponieważ wymagane techniki są zbyt duże.
Technicznie std::function
może użyć magicznego pyłu bajki robić to, co robi, a jego zachowanie jest opisana w normie, a nie jego realizację. Opisuję prostą implementację, która jest zbliżona do zachowania implementacji std::function
, z którą współpracowałem.
Dziękuję, to jest bardzo kompletne. +1 –
Wierzę, że można również przekazać funkcję "const std :: function & func". Czy jest lepiej/gorzej niż ruch i ... rzecz? Zawsze myślałem, że "const &" oznacza po prostu "przekazanie tego obiektu", ale && oznacza przeniesienie stanu obiektu do nowej instancji (która może wywołać ctor kopii ruchu?) –
@AlexPetrenko '&&' jest zwykle wartością rvalue, ale jest również używany w technice zwanej ["perfect forwarding"] (http://stackoverflow.com/questions/3582001/advantages-of-using-forward)/["universal references"] (http://stackoverflow.com/ questions/14302849/syntax-for-universal-references), gdzie 'T &&' może być wartością rwartującą ** lub ** wartość odniesienia l (w kontekście dedukcji typu - technicznie w dowolnym miejscu, ale rzadko jest używana poza odliczeniem typu w ten sposób). – Yakk