Как хранится контекст у std::function?
От: DTF  
Дата: 16.01.17 22:37
Оценка:
Доброй ночи.

Допустим, есть вот такая функция:
//считаем, что у всех T, с которыми вызывают функцию, есть преобразование в int

template <typename T>
std::function<int(int)> maker(T param) {
   return [param](int x) {return x + param; };
}


Далее она используется как-то так:
auto f1 = maker(1);
auto f2 = f1;

///... какая-то работа с f1 и f2


Насколько я понимаю, увидев лямбда-функцию, компилятор создаст специальный объект, одним из полей которого и будут данные, переданные в param.
Далее этот объект будет храниться где-то в недрах std::function.

А далее вопросы (если это где-то уже обсуждалось — киньте ссылкой, пожалуйста):
1. каково время жизни этого объекта?
2. Где он создается — на стеке или в куче? Или он вообще static?
3. Для нескольких вызовов maker с одинаковым параметром будет создано несколько лямбда-объектов, или будет использован один и тот же?
4. Как std::function хранит внутри себя неизвестный объем данных? Ведь при объявлении f2 ничего не известно про то, какой контекст будет у соответствующей std::function. Как компилятор понимает, сколько памяти надо выделить под f2? Или внутри просто pimpl на нужный объект?

5. Что происходит при присваивании f2 = f1? Происходит ли копирование лямбда-объекта?
Re: Как хранится контекст у std::function?
От: Vamp Россия  
Дата: 17.01.17 02:57
Оценка:
DTF>Насколько я понимаю, увидев лямбда-функцию, компилятор создаст специальный объект, одним из полей которого и будут данные, переданные в param.
DTF>Далее этот объект будет храниться где-то в недрах std::function.
Сначала этот объект будет хранится в автоматической памяти, созданной для функции maker. Потом он будет скопирован в объект (копирующим конструктором), созданный внутри std::function. Первый помрет после заверешния работы функции, второй будет жить, пока живет std::function.

DTF>1. каково время жизни этого объекта?

См. выше
DTF>2. Где он создается — на стеке или в куче? Или он вообще static?
Он точно не static. Куча или стек зависит от того, насколько велик объект и какой его тип. Если объект имеет тип указатель на функцию или reference_wrapper, (не твой случай), то гарантированно используется small object optimization, и копия размещается в std::function, динамическая память не используется. В противном случае, в зависимости от реализации и размера объекта, он может быть аллоцирован как в динамической памяти, так и внутри std::function.

DTF>3. Для нескольких вызовов maker с одинаковым параметром будет создано несколько лямбда-объектов, или будет использован один и тот же?

Несколько (см. выше сколько), но все одного и того же (неизвестного) типа.

DTF>4. Как std::function хранит внутри себя неизвестный объем данных? Ведь при объявлении f2 ничего не известно про то, какой контекст будет у соответствующей std::function. Как компилятор понимает, сколько памяти надо выделить под f2? Или внутри просто pimpl на нужный объект?

Там используется несложный type erasure. Внутри std::function определен тип с виртуальным оператором () (его аргументы и возвращаемый тип определяются шаблонным параметром std::function). Шаблонный конструктор function создает тип-наследник этого типа (через инстанцирование шаблона), и аллоцирует объект такого типа, либо с помощью new, либо placement new.

DTF>5. Что происходит при присваивании f2 = f1? Происходит ли копирование лямбда-объекта?

Да. Копируется из одного объекта в другой.
Да здравствует мыло душистое и веревка пушистая.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.