std::function
-
Klaus82 schrieb:
Was ist so toll an
std::function
(zusammen mitstd::bind
?)Man kann damit funktionale Programmierung in C++ betreiben. Funktionen können partially applied (std::bind) und wie normale Werte herumgereicht (std::function) werden.
Mit mehr Aufwand geht sowas auch ohne diese features, aber es wird mit ihnen halt einfacher. Das Strategy-Pattern z.B. wird leichter lesbar wenn man nicht mehr wie blöde Rumableiten muss, sondern einfach ein passende neue Strategie-Funktion schreiben kann. Und nakte Funktionszeiger sind hässlich.
-
std::function ist ein nützliches Werkzeug, direkter Vererbung aus dem Weg zu gehen. Man erinnere sich: Vererbung ist die stärkste Form der Bindung, d.h. verhindert gute Kapselung.
Ausgangslage: Ich will eine Klasse schreiben, die zum ableiten vorgesehen ist (Java ist voll von solchen Klassen: Runnable, Callable, etc.)
1. -> Warum ist hier std::function nicht angemessen?
2. -> Anstatt std::function einen Template-Parameter nehmen.TyRoXx schrieb:
Statt
bind
sollte man in C++14 lieber Lambdas verwenden. Das spart beispielsweise infunction
einen Funktionszeiger an Speicher.Quelle? [Tipp: Es ist falsch]
-
#include <array> #include <iostream> #include <functional> int main() { typedef std::array<int, 1> bound_type; bound_type const bound{}; void (*c)(bound_type) = [](bound_type) {}; std::cerr << "function ptr: " << sizeof(c) << '\n'; auto a = [](bound_type) {}; std::cerr << "stateless lambda: " << sizeof(a) << '\n'; auto a_bound = std::bind(a, bound); std::cerr << "lambda bound with std::bind: " << sizeof(a_bound) << '\n'; auto b = [bound]() {}; std::cerr << "lambda-based closure: " << sizeof(b) << '\n'; auto c_bound = std::bind(c, bound); std::cerr << "function ptr bound with std::bind: " << sizeof(c_bound) << '\n'; }
GCC 4.8 schrieb:
function ptr: 8
stateless lambda: 1
lambda bound with std::bind: 8
lambda-based closure: 4
function ptr bound with std::bind: 16Lambda ohne
bind
ist am kleinsten und hat keinen Speicher-Overhead. Das kann in einerfunction
den Unterschied zwischen dynamischer Speicheranforderung und Small Functor Optimization ausmachen.std__function=virtual schrieb:
TyRoXx schrieb:
Statt
bind
sollte man in C++14 lieber Lambdas verwenden. Das spart beispielsweise infunction
einen Funktionszeiger an Speicher.Quelle? [Tipp: Es ist falsch]
Lambda spart sogar noch mehr als den Funktionszeiger, nämlich 4 Bytes Padding (warum auch immer GCC die einfügt).
-
TyRoXx schrieb:
std__function=virtual schrieb:
TyRoXx schrieb:
Statt
bind
sollte man in C++14 lieber Lambdas verwenden. Das spart beispielsweise infunction
einen Funktionszeiger an Speicher.Quelle? [Tipp: Es ist falsch]
Lambda spart sogar noch mehr als den Funktionszeiger, nämlich 4 Bytes Padding (warum auch immer GCC die einfügt).
Äh, ich hatte dich so verstanden, man sollte Lambdas verwenden um std::function zu erzeugen.
Wer die Wahl zwischen Lambdas und std::function hat, nimmt natürlich Lambdas. Allerdings nicht wegen dem Speicher-Overhead, der sowieso vernachlässigbar ist, sondern wegen der doppelten Indirektion.
-
std__function=virtual schrieb:
Äh, ich hatte dich so verstanden, man sollte Lambdas verwenden um std::function zu erzeugen.
Ja, das sollte man.
Man könnte sogar in diesem Fall Lambda benutzen, wenn man es
function
einfacher machen möchte://schlecht std::function<int ()>{std::rand}; //gut std::function<int ()>{[]{ return std::rand(); }};
Dass das Speicher spart, setzt leider noch Compiler-spezifische Erweiterungen voraus, die in der Standardbibliothek ausgenutzt werden. Keine Ahnung, ob das jemand so implementiert. Naja gut, mit Erweiterungen kann auch die erste Variante optimiert werden, also ein schlechtes Beispiel.
Mal sehen, ob man bei
bind
auch die Kopien der Konstanten wegbekommen kann:#include <iostream> #include <functional> #include <type_traits> #include <cstdio> template <class F, F f> struct constified { template <class ...Args> auto operator ()(Args &&...args) const { return f(std::forward<Args>(args)...); } }; int main() { auto put_a = std::bind(std::putc, 'a', stderr); std::cerr << "put_a: " << sizeof(put_a) << '\n'; //Der Funktionszeiger wird Teil des Funktortyps. //std::bind könnte dann Empty Base Optimization anwenden und man //hätte Speicher gespart. auto put_b = std::bind(constified<int (*)(int, FILE *), std::putc>(), 'b', stderr); std::cerr << "put_b: " << sizeof(put_b) << '\n'; auto put_c = [] { return std::putc('c', stderr); }; std::cerr << "put_c: " << sizeof(put_c) << '\n'; put_a(); put_b(); put_c(); std::cerr << '\n'; }
Mein GCC macht die Optimierung leider nicht. Entweder hat man die bisher vergessen oder der Standard verbietet sie aus irgendeinem Grund.
put_a: 24 put_b: 24 put_c: 1 abc
Lambdas machen das ohne jede Hilfe oder Optimierung der Standardbibliothek optimal.
-
Ich verstehe nicht, was du mit deinem zweiten Teil aussagen willst (es kommt nicht ein einziges mal der Typ std::function vor), aber std::bind so zu implementieren, dass es ein Lambda zurückgibt ist in C++14 trivial, deshalb sehe ich nicht ein, wie Lambdas da schneller sein können.
-
std__function=virtual schrieb:
Ich verstehe nicht, was du mit deinem zweiten Teil aussagen willst (es kommt nicht ein einziges mal der Typ std::function vor),
Ich will damit sagen, dass Konstanten von Lambdas automatisch richtig behandelt werden.
bind
kopiert Konstanten unnötigerweise und bläht den Funktor damit auf.
Das ist genau dann relevant, wenn man den Funktor in einefunction
steckt. Dann finden sich die unnötigen Kopien nämlich auf dem Heap wieder.function
wird typischerweise so implementiert, dass kleine Funktoren imfunction
-Objekt direkt gespeichert werden, also ohne dynamische Speicheranforderung und ohne Indirektion. Mit Lambdas ist die Wahrscheinlichkeit höher, dass man von dieser Optimierung profitiert, denn Lambda-Closures sind oft kleiner als diebind
-Äquivalente.std__function=virtual schrieb:
aber std::bind so zu implementieren, dass es ein Lambda zurückgibt ist in C++14 trivial, deshalb sehe ich nicht ein, wie Lambdas da schneller sein können.
Weil auch bei einem Lambda die Argumente gebunden werden müssen. Die gebundene Kopie von
f
wird Speicher verbrauchen:template <class F, class ...Args> auto bind(F f, Args ...args) { return [f, args...] { return f(args); }; }
Der Compiler kann so schlau sein das zu optimieren. Darauf würde ich mich aber nicht verlassen.
-
return [=] { return f(args...); };
Das haben wir schon einmal durchdiskutiert..
Ich will damit sagen, dass Konstanten von Lambdas automatisch richtig behandelt werden.
Lambdas verbrauchen Maschinencode. Der Closure-Typ ist eine Klasse mit überladenem operator(), welcher natürlich als gewöhnliche Funktion im Programm fungiert. Allerdings nur einmal, wer also den Funktor kopiert...
-
Ich sehe, das übersteigt mal wieder meine Fähigkeiten.
Ich benötige Funktionen vorwiegend für meine Numerik und muss sie in numerische Bibliothen stopfen, aus dem Grund bin ich immer bestrebt diese Schnittstelle möglichst einfach zu halten ... und zu verstehen.
Ich bin zuletzt auf
std::function
aufmerksamt geworden aus diesem Grund.Gruß,
-- Klaus.
-
Klaus82 schrieb:
Ich bin zuletzt auf std::function aufmerksamt geworden aus diesem Grund.
In dem Fall geht es darum, eine Klassenmethode mittels
std::function<>()
zu wrappen. Dort istbind()
noetig, um den this Zeiger mit der Methode zu binden, was, wie Tyroxx schon ganz am Anfang geschrieben hat, die Konstruktion (hier: methode einer Klasse) von deren Verwendung entkoppelt.