У меня есть различные функции в моей кодовой базе, которые принимают общий вызываемый объект и передают его ряду вложенных лямбд, прежде чем вызывать его. Пример:
template <typename TF>
void interface(TF&& f)
{
nested0([/*...*/]()
{
nested1([/*...*/](auto& x)
{
nested2([&x, /*...*/]()
{
f(x);
});
});
});
}
Обратите внимание, что interface
принимает вызываемый объект типа TF
перенаправив ссылку (ранее известный как универсальная ссылка). Вызываемый объект — это обычно лямбда с различными захваченными переменными, как по значению, так и по ссылке.
Каков наилучший (с точки зрения производительности) способ захвата f
во вложенных лямбдах при сохранении правильности?
Я могу придумать три варианта:
Захватить f
по копии.
nested0([f]()
{
nested1([f](auto& x)
{
nested2([&x, f]()
{
f(x);
});
});
});
Может вызвать ненужные копии, и если захваченный объект mutable
это может вызвать неправильное поведение.
Захватить f
по ссылке.
nested0([&f]()
{
nested1([&f](auto& x)
{
nested2([&x, &f]()
{
f(x);
});
});
});
Кажется разумным, но может вызвать проблемы, если какая-либо из вложенных лямбд выполняет действие, которое переживает владельца f
, Представь если nested2
тело было выполнено в отдельном потоке — f
уже может быть вне области.
Сделать лямбды mutable
и захватить совершенной пересылкой.
#define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
nested0([f = FWD(f)]() mutable
{
nested1([f = FWD(f)](auto& x) mutable
{
nested2([&x, f = FWD(f)]() mutable
{
f(x);
});
});
});
Лямбды должны быть mutable
потому что мы потенциально движемся f
из лямбды в другую. Этот подход, по-видимому, позволяет избежать ненужных копий и правильно перемещать вызываемый объект, если он должен пережить первоначальный вызывающий объект.
Вариант 3 всегда лучший или у него есть потенциальный недостаток? …или, может быть, нет «лучшего и правильного» способа вообще (знание об вызываемом объекте обязательно)?
Как упоминалось в комментариях, трудно сказать с таким плохим контекстом, данным о проблеме.
Тем не менее, мне кажется, что лучшим решением будет захватить все по ссылке и нарушать эту логику всякий раз, когда вам нужна копия, цель которой — пережить время жизни f
, В качестве примера:
nested0([&f]() {
n1([&f](auto& x) {
n2([&x,
// get a local copy of f
f{std::forward<TF>(f)}]() {
f(x);
});
});
});
Во всяком случае, для такой проблемы не существует практического правила.
Лучший Решение тесно связано с реальной проблемой.
По-прежнему. Справедливо.
Других решений пока нет …