La mise en œuvre de l' std::function
peuvent varier d'une implémentation à une autre, mais l'idée de base est qu'il utilise le type-erasure. Bien qu'il existe de multiples façons de le faire, vous pouvez imaginer un triviale (non optimale) solution pouvait être comme ça (simplifié pour le cas spécifique de l' std::function<int (double)>
, par souci de simplicité):
struct callable_base {
virtual int operator()(double d) = 0;
virtual ~callable_base() {}
};
template <typename F>
struct callable : callable_base {
F functor;
callable(F functor) : functor(functor) {}
virtual int operator()(double d) { return functor(d); }
};
class function_int_double {
std::unique_ptr<callable_base> c;
public:
template <typename F>
function(F f) {
c.reset(new callable(f));
}
int operator()(double d) { return c(d); }
// ...
};
Dans cette approche simple de l' function
objet serait de stocker juste un unique_ptr
d'un type de base. Pour chaque foncteur utilisé avec l' function
, un nouveau type dérivé de la base est créée et un objet de ce type instanciés dynamiquement. L' std::function
objet est toujours de la même taille et va allouer de l'espace nécessaire pour les différents foncteurs dans le tas.
Dans la vraie vie, il y a différentes optimisations que de fournir des avantages de performance, mais ne ferait que compliquer la réponse. Le type peut utiliser de petits objets, des optimisations, la répartition dynamique peut être remplacé par un libre-pointeur de fonction qui prend le foncteur comme argument pour éviter un niveau d'indirection... mais l'idée est fondamentalement la même.
Concernant la question de comment les copies de l' std::function
se comporter, un test rapide indique que des copies de l'intérieur de l'objet appelable sont fait, plutôt que de partager l'état.
// g++4.8
int main() {
int value = 5;
typedef std::function<void()> fun;
fun f1 = [=]() mutable { std::cout << value++ << '\n' };
fun f2 = f1;
f1(); // prints 5
fun f3 = f1;
f2(); // prints 5
f3(); // prints 6 (copy after first increment)
}
Le test indique qu' f2
obtient une copie de la appelable entité, plutôt qu'une référence. Si le callable entité a été partagée par les différents std::function<>
objets, la sortie du programme aurait été de 5, 6, 7.