В выражении
auto p = vglambda([](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; });
объект закрытия vglambda
инициализируется принтером объекта закрытия, тип которого соответствует лямбда-выражению
[](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; }
Мне кажется более правильным сказать, что vglamba
вызывается , вызывается с помощью лямбды, которая использует std::cout
.vglambda
инициализируется универсальной лямбда-функцией, которая получает универсальное (auto
) значение (printer
).
Внутри printer
, вложенное (анонимное)) лямбда-выражение
return [=](auto&&... ts){}
захватывает принтер по копии и его пакет параметров в качестве ссылки на значение.
Не внутри printer
(это только аргумент лямбды), но внутрилямбда, которая сохраняется в переменной vglambda
.
Да, анонимная вложенная универсальная и переменная функция захватывает printer
по значению, но не является точным, что ... ts
фиксируются (являются аргументами) ипо ссылке rvalue.
Вложенная лямбда почти эквивалентна шаблонной функции (ну ... структуре с шаблоном operator()
внутри нее ... но для упрощения ...)
template <typename ... Ts>
auto func (Ts && ... ts)
{ /*...*/ }
В этом случае &&
не являются ссылками rvalue, а forwarding ссылками (см. эту страницу для получения дополнительной информации), так какВы можете увидеть изнутри использование std::forward
.
Это важныйточка, но посмотри дальше.
Внутри тела (анонимного) лямбда-выражения выражение
printer(std::forward<decltype(ts)>(ts)...);
перенаправляет пакет параметров в printer
[что, по сути, похоже на вызов принтераиспользование operator ()
]
Это мне кажется правильным.
В последнем выражении внутри тела (анонимного) лямбда-выражения (анонимного) нулевая лямбдаПохоже, выражение захватывает объект закрытия printer
из охватывающей области путем копирования вместе с пакетом параметров и вызывает объект закрытия printer
с перенаправленным пакетом параметров.
return [=] { printer(ts...); };
Thisмне кажется правильным, но вы должны увидеть здесь проблему (вместе с предыдущим вызовом printer()
.
, почему в теле (анонимного) есть две разные строки вызова объекта закрытия принтералямбда-выражение, одно без (анонимного) нулевого лямбда-выражения, а другое внутри?
Посмотрите, как используется p
auto p = vglambda([](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; });
auto q = p(1, 'a', 3.14);
q();
Как инициализируется p
?
p
инициализируется с помощью общей и вариационной лямбды, определенной внутри лямбды, которая инициализирует vglambda
.Поэтому, когда он называется
auto q = p(1, 'a', 3.14);
, у вас получается, что универсальная лямбда-переменная вызывается с помощью набора переменных ts...
, который расширяется до 1
, 'a'
и 3.14
.
* 1095.* Итак, вызывая
p(1, 'a', 3.14)
, у вас есть то, что вызывается (игнорируя пересылающую часть)
printer(1, 'a', 3.14);
(где printer()
- это лямбда, что std::cout
a
, b
и c
) и , что возвращается [=] { printer(1, 'a', 3.14); }
.
Итак, q
инициализируется с помощью [=] { printer(1, 'a', 3.14); }
, и вызывается
q();
printer(1, 'a', 3.14)
снова.
Таким образом, идея универсальной и вариационной лямбды состоит в том, чтобы вызывать print()
с полученным аргументом вариады, в первый раз и возвращают другую лямбду, которая снова print()
при вызове.
Таким образом, с p(1, 'a', 3.14)
вы активируете первый print()
(тот, у которого std::forward
), и каждый раз, когда вы вызываете возвращаемое значение (q
, в вашем примере), вы активируете второе print()
(без std::forward
).
Но в вашем коде есть большой дефект.Дефект, который не вызывает проблем при вызове p()
с примитивными типами как int
, char
и double
.Но дефект, который опасен при использовании сложных объектов, поддерживающих семантику перемещения.
Проблема в том, что, используя std::forward
, вы можете активировать семантику перемещения.
Итак, в этом коде
return [=](auto&&... ts) // generic lambda, ts is a parameter pack
{
printer(std::forward<decltype(ts)>(ts)...);
return [=] { printer(ts...); }; // <- DANGER: unsafe use of `ts...`
};
первый printer()
вызов безопасен и корректен (используется std::forward
), но второй вызов printer()
опасен, потому что мы не знаем, можно ли по-прежнему использовать ts...
.
Используя ts...
два раза, я предлагаю переписать лямбду следующим образом
return [=](auto const & ... ts)
{
printer(ts...);
return [=] { printer(ts...); };
};