Мне нужна некоторая информация о том, как правильно думать о закрытиях С++ 11 и std::function
в том, как они реализованы и как обрабатывается память.
Несмотря на то, что я не верю в преждевременную оптимизацию, у меня есть привычка тщательно анализировать влияние моих решений при написании нового кода. Я также делаю довольно много программирования в реальном времени, например. на микроконтроллерах и в аудиосистемах, где следует избегать периодов нераспределения памяти/освобождения памяти.
Поэтому я хотел бы лучше понять, когда использовать или не использовать С++ lambdas.
Мое настоящее понимание заключается в том, что лямбда без захваченного закрытия точно напоминает обратный вызов C. Однако, когда среда захватывается либо по значению, либо по ссылке, анонимный объект создается в стеке. Когда значение-замыкание должно быть возвращено из функции, одно обертывает его в std::function
. Что происходит с закрывающей памятью в этом случае? Скопирован ли он из стека в кучу? Освобождается ли он всякий раз, когда освобождается std::function
, т.е. Считается ли оно отсчетным как a std::shared_ptr
?
Я предполагаю, что в системе реального времени я мог бы создать цепочку лямбда-функций, передав B в качестве аргумента продолжения A, чтобы был создан конвейер обработки A->B
. В этом случае замыкания А и В будут выделены один раз. Хотя я не уверен, будут ли они выделены в стеке или куче. Однако в целом это кажется безопасным для использования в системе реального времени. С другой стороны, если B создает некоторую лямбда-функцию C, которую он возвращает, то память для C будет распределяться и освобождаться повторно, что неприемлемо для использования в режиме реального времени.
В псевдокоде, цикле DSP, который, я думаю, будет безопасным в режиме реального времени. Я хочу выполнить блок обработки A, а затем B, где A вызывает его аргумент. Обе эти функции возвращают объекты std::function
, поэтому f
будет объектом std::function
, где его среда хранится в куче:
auto f = A(B); // A returns a function which calls B
// Memory for the function returned by A is on the heap?
// Note that A and B may maintain a state
// via mutable value-closure!
for (t=0; t<1000; t++) {
y = f(t)
}
И тот, который, я думаю, может быть плохим для использования в режиме реального времени:
for (t=0; t<1000; t++) {
y = A(B)(t);
}
И тот, где, я думаю, стек стека, вероятно, используется для закрытия:
freq = 220;
A = 2;
for (t=0; t<1000; t++) {
y = [=](int t){ return sin(t*freq)*A; }
}
В последнем случае замыкание строится на каждой итерации цикла, но, в отличие от предыдущего примера, оно дешево, потому что оно подобно вызову функции, никакие распределения кучи не производятся. Более того, мне интересно, может ли компилятор "поднять" закрытие и сделать оптимизацию вложения.
Это правильно? Спасибо.