Реалізація std::function
може відрізнятися від однієї реалізації до іншої, але основна ідея полягає в тому, що вона використовує стирання типів. Хоча існує кілька способів зробити це, ви можете уявити собі тривіальне (не оптимальне) рішення, яке може бути таким (спрощене для конкретного випадку std::function<int (double)>
заради простоти):
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>(f));
}
int operator()(double d) { return c(d); }
// ...
};
У цьому простому підході function
об'єкт зберігав би лише unique_ptr
базовий тип. Для кожного різного функтора, що використовується з function
, створюється новий тип, похідний від основи, і об'єкт цього типу створюється динамічно. std::function
Об'єкт завжди одного і того ж розміру і виділити простір по мірі необхідності для різних функторів в купі.
У реальному житті існують різні оптимізації, які забезпечують переваги в роботі, але ускладнюють відповідь. Тип може використовувати оптимізацію невеликих об’єктів, динамічне відправлення може бути замінено покажчиком вільної функції, який приймає функтор як аргумент, щоб уникнути одного рівня опосередкованості ... але ідея в основному однакова.
Щодо питання того, як std::function
поводяться копії поведінки, швидкий тест вказує, що виконуються копії внутрішнього об'єкта, що викликається, а не спільний доступ до стану.
// 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)
}
Тест показує, що f2
отримує копію об'єкта, що викликається, а не посилання. Якби об'єкт, що викликається, був спільним для різних std::function<>
об'єктів, результат програми був би 5, 6, 7.
std::function
деякий час тому. По суті, це клас дескриптора для поліморфного об’єкта. Похідний клас внутрішнього базового класу створюється для зберігання параметрів, виділених у купі, - тоді вказівник на нього зберігається як суб'єктstd::function
. Я вважаю, що він використовує підрахунок посилань, якstd::shared_ptr
для обробки копіювання та переміщення.