Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Homunculus, Вы писали:
H>>Аргумент делегата
S>Делегата? А что в C++ их добавили? Или имеешь в виду ссылка на функцию?
А тебя тело функции внутри круглых скобок не смутило?
Здравствуйте, Shmj, Вы писали:
S>Кто может ткнуть носом, что значит [socket] — что за конструкция?
Захват переменной по значению, которую можно использовать в коде лямбды (замыкания).
Еще варианты:
[] — ничего не захватываем
[=] — захватываем все локальные переменные по значению
[&] — захватываем все локальные переменные по адресу
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, pilgrim_, Вы писали:
_>>[&] — захватываем все локальные переменные по адресу
M>Только по ссылке. Ну, и после общего указания, можно ещё через запятую захват конкретных переменных сделать
С тем учтонением, что если "общее указание" — захват по ссылке, то конкретные перемнные можно указать только для захвата по значению. И наоборот.
Почему было введено такое ограничение — навскидку не скажу.
Здравствуйте, andrey.desman, Вы писали:
AD>Здравствуйте, pilgrim_, Вы писали:
_>>[=] — захватываем все локальные переменные по значению _>>[&] — захватываем все локальные переменные по адресу
AD> Тут важно уточнить, что не все, а все используемые.
Здравствуйте, Marty, Вы писали:
M>Все видимые в точке захвата, не?
Не. Все используемые.
M>Что значит — используемые?
Которые используются в теле лямбды.
Здравствуйте, andrey.desman, Вы писали:
M>>Все видимые в точке захвата, не? AD>Не. Все используемые.
M>>Что значит — используемые? AD>Которые используются в теле лямбды.
Здравствуйте, Pzz, Вы писали:
Pzz>Это анонимная функция (функция есть, а имени у нее нету), описанная прям по месту использования. В нонешнем C++ и не такое бывает.
Не функция, а функтор. Класс с перегруженным оператором ().
А код ТСа в ++03 был бы записан примерно так:
struct Anon {
Socket socket;
Anon(Socket socket): socket(socket) {}
void operator()(std::vector<unsigned char> data) {
auto task = Async(process1, data); // ну тут не auto понятно
process(data);
task.wait();
socket.send(data, kNoCallback);
}
}
...
socket.receive(Anon(socket));
И он на самом деле именно это и означает, просто такой вот сахарок.
Кстати ++03 ещё и не давал структуры с методами имплементировать внутри тела функции, требовал вытаскивать кишки наружу.
Я к тому, чтоб ТС оценил удобство.
Здравствуйте, T4r4sB, Вы писали:
Pzz>>Это анонимная функция (функция есть, а имени у нее нету), описанная прям по месту использования. В нонешнем C++ и не такое бывает.
TB>Не функция, а функтор. Класс с перегруженным оператором ().
Ну я понимаю, что в сишном рантайме настоящую функцию на ходу не создашь. Но логически это именно функция.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>С тем учтонением, что если "общее указание" — захват по ссылке, то конкретные перемнные можно указать только для захвата по значению. И наоборот.
M>Разве?
Именно так.
[&, i] — допустимо. Общий захват — по ссылке, i — по значению.
[&, &i] — ошибка. Нельзя захватывать i по ссылке, если общий захват и так по ссылке.
[=, i] — ошибка. Нельзя захватывать i по значению, если общий захват и так по значению.
[=, &i] — допустимо. Общий захват — по значению, i — по ссылке.
Здравствуйте, kaa.python, Вы писали:
KP>Всегда хотел спросить, а тут такая возможность! А какие ищущения испытывает человек когда его из криокамеры выпускают?
В C# такая концепция есть, по этому не вставило. Радость от того что в C++ пока не добавили деревья выражений — вот там действительно перелом сознания.
Здравствуйте, Pzz, Вы писали:
Pzz>Это анонимная функция (функция есть, а имени у нее нету), описанная прям по месту использования. В нонешнем C++ и не такое бывает.
Здравствуйте, Marty, Вы писали:
M>>>Что значит — используемые? AD>>Которые используются в теле лямбды. M>Это уже оптимизация
Чтобы понять почему так, создай класс с говорящим деструктором, создай несколько объектов этого класса, вызови лямбду c [=] и посчитай вызовы деструкторов.
Здравствуйте, T4r4sB, Вы писали:
TB>Кстати ++03 ещё и не давал структуры с методами имплементировать внутри тела функции, требовал вытаскивать кишки наружу.
Это не так, такое работало даже в C++98. Были ограничения, да, например связанные с шаблонами, но конкретно описываемый пример работает.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
Pzz>>Ну я понимаю, что в сишном рантайме настоящую функцию на ходу не создашь
EP>Рантайм тут вообще ортогонален, ибо весь необходимый код генерируется статически, без всякой поддержки рантайма.
Ну вот в Go лямбда-выражения, со всякими там замыканиями и прочими делами, выдают абсолютно полноценный указатель на функцию. Ровно такой же, как если эту функцию (с совместимыми типами параметров, разумеется) статически описать и использовать ее имя в качестве значения.
Очевидно, что это связано с тем, на что именно там, под капотом, указывают указатели на функцию.
А в Си, где указатель на функцию является в буквальном смысле адресом перехода, замыканий и прочих неявных данных к нему не приделаешь. Это именно что ограничение рантайма. Рантайма не в смысле набора функций из стандартной библиотеки а в смысле среды, в которой исполняется пользовательский код.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Чтобы понять почему так, создай класс с говорящим деструктором, создай несколько объектов этого класса, вызови лямбду c [=] и посчитай вызовы деструкторов.
С одной стороны так-то оно так, а с другой стороны нельзя расчитывать на побочные эффекты создания и уничтожения объектов, и напороться на какой-нибудь copz elision.
Здравствуйте, Hobbes, Вы писали:
EP>>Чтобы понять почему так, создай класс с говорящим деструктором, создай несколько объектов этого класса, вызови лямбду c [=] и посчитай вызовы деструкторов. H>С одной стороны так-то оно так, а с другой стороны нельзя расчитывать на побочные эффекты создания и уничтожения объектов, и напороться на какой-нибудь copz elision.
В первую очередь здесь дело не в побочных эффектах, а в том что именно произойдёт [=] в функции с десятком локальных переменных. И вот если бы всегда захватывалось всё — то приходилось бы всё время расписывать всё вручную, а так как захватывается только то что используется — то можно без проблем использовать [=].
H>а с другой стороны нельзя расчитывать на побочные эффекты создания и уничтожения объектов
Можно, здесь семантика вполне себе определенна. Естественно классы предоставляющие это должны иметь корректную copy/move semantic, и тогда никакой elision не помеха.
Здравствуйте, Pzz, Вы писали:
Pzz>А в Си, где указатель на функцию является в буквальном смысле адресом перехода, замыканий и прочих неявных данных к нему не приделаешь. Это именно что ограничение рантайма. Рантайма не в смысле набора функций из стандартной библиотеки а в смысле среды, в которой исполняется пользовательский код.
При необходимости можно и на C++ конвертировать замыкания с состоянием в указатель на функцию. Если оставаться в рамках стандартна, то будут некоторые ограничения. Если же выйти немного за рамки стандарта, и ограничиться конечным набором целевых платформ, то можно и вовсе без ограничений.
Тем не менее, "логически" это всё равно не функция, а замыкание (или делегат) — ибо имеется вполне себе осязаемое состояние.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Тем не менее, "логически" это всё равно не функция, а замыкание (или делегат) — ибо имеется вполне себе осязаемое состояние.
Вот тебе совершенно классическая сишная функция с состоянием:
int NextI (void)
{
static int i;
return i++;
}
Присутствие состояния это не то, что отличает функцию от не-функции.
Здравствуйте, Pzz, Вы писали:
EP>>Тем не менее, "логически" это всё равно не функция, а замыкание (или делегат) — ибо имеется вполне себе осязаемое состояние. Pzz>Вот тебе совершенно классическая сишная функция с состоянием: Pzz> static int i;
Это глобальное состояние, пусть и с ограниченной областью видимости, а у замыкания состояние локально для конкретного объекта. Я выше не просто так привел пример с копированием состояния "fork"
Здравствуйте, Evgeny.Panasyuk, Вы писали:
Pzz>>А в Си, где указатель на функцию является в буквальном смысле адресом перехода, замыканий и прочих неявных данных к нему не приделаешь. Это именно что ограничение рантайма. Рантайма не в смысле набора функций из стандартной библиотеки а в смысле среды, в которой исполняется пользовательский код.
EP>При необходимости можно и на C++ конвертировать замыкания с состоянием в указатель на функцию. Если оставаться в рамках стандартна, то будут некоторые ограничения.
Здравствуйте, night beast, Вы писали:
EP>>При необходимости можно и на C++ конвертировать замыкания с состоянием в указатель на функцию. Если оставаться в рамках стандартна, то будут некоторые ограничения. NB>как? NB>можно пример?
Например. Если оставаться в рамках стандарта, то чтобы получить тысячу уникальных адресов функций, надо пред-определить тысячу функций на этапе компиляции. Что каждая из этих тысячи функций будет делать — может вполне себе зависеть от runtime состояния, как в примере.
Ограничение здесь это максимальное число одновременно живущих адресов функций — вполне терпимо учитывая что лимит контролируем и этот трюк нужен разве что для какого-то кривого C API который не удосужился в довесок к указателю на функцию взять хотя бы void* data.
Если выйти за рамки стандарта, то можно просто генерировать trampoline код налету (не забыв про DEP).
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Например. Если оставаться в рамках стандарта, то чтобы получить тысячу уникальных адресов функций, надо пред-определить тысячу функций на этапе компиляции. Что каждая из этих тысячи функций будет делать — может вполне себе зависеть от runtime состояния, как в примере.
не, ну так не интересно.
я думал что-то более нормальное есть...
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>[&, i] — допустимо. Общий захват — по ссылке, i — по значению. АТ>[&, &i] — ошибка. Нельзя захватывать i по ссылке, если общий захват и так по ссылке. АТ>[=, i] — ошибка. Нельзя захватывать i по значению, если общий захват и так по значению. АТ>[=, &i] — допустимо. Общий захват — по значению, i — по ссылке.
АТ>http://eel.is/c++draft/expr.prim.lambda#capture-2 АТ>http://coliru.stacked-crooked.com/a/9fd6075650e3b6b8
Мне хорошо запомнилась статья на "хабре", которую использую уже несколько лет, в качестве руководства: https://habr.com/ru/post/66021
Здравствуйте, AlexGin, Вы писали:
AG>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>[&, i] — допустимо. Общий захват — по ссылке, i — по значению. АТ>>[&, &i] — ошибка. Нельзя захватывать i по ссылке, если общий захват и так по ссылке. АТ>>[=, i] — ошибка. Нельзя захватывать i по значению, если общий захват и так по значению. АТ>>[=, &i] — допустимо. Общий захват — по значению, i — по ссылке.
АТ>>http://eel.is/c++draft/expr.prim.lambda#capture-2 АТ>>http://coliru.stacked-crooked.com/a/9fd6075650e3b6b8
AG>Мне хорошо запомнилась статья на "хабре", которую использую уже несколько лет, в качестве руководства: AG>https://habr.com/ru/post/66021
Статья по ссылке содержит ту же ошибку:
[&, x, &y] // захват всех переменных по ссылке, кроме x…
Нет, так в С++ не разрешается.
Возможно дело в том, что статься писалась в 2009, то есть до выхода финальной версии стандарта и в черновике подобное еще допускалось. Как я сказал выше, это выглядит как вполне логичная возможность. Меня удивляет, что ее запретили. Интересно было бы узнать, почему.
---
Порылся в черновиках: нет, даже самые ранние версии черновика С++11, в которых уже появилось описание лямбда-выражений (N2588, 2008 год), сразу запрещали такую избыточность в захватах. То есть статья на хабре по-видимому писалась очередным "практиком" и любителем "де факто", изучавшим С++ по разудалому творчеству GNU-шных студентов: GCC по умолчанию рапортует эту ошибку через "warning".
Опять же, мне это ограничение кажется странным (как, видать, и "GNU-шным студентам").
H>>Аргумент делегата S>Делегата? А что в C++ их добавили? Или имеешь в виду ссылка на функцию?
Ещё в прошлом тысячелетии. Только в C# делегат это какая-то суперсущность которая по итогу все равно костыль. Делегатом в нормальном коде является любая сущность которой делегирут работу. Как правило это просто интерфейс (которые в C++ разумеется тоже добавили... в прошлом тысячелетии). Разница между хэндлером и делегатом — как правило исключительно в уме автора. Иногда — существуют и хэндлеры и делегаты одновременно реализуя один и тот же интерфейс. Опять же — разница умозрительная. {Ломает мозг? Ну, так, это потому, что некоторые вещи в C# названы в чудовищном отрыве от реальности без како1-либо неоходимости. Событие? Событие, извините, это не конструкция языка.}
Ну а про аргумент делегата — это и вовсе какая-то невесёлая шутка. Где там аргумент я не увидел, и как бы я мог... У функции — параметры! Аргументы — это значения параметров. Это очень просто на самом деле. Как только перестанете путать парамеры с аргументами в своей голове — я гарантирую: начнете плеваться и зубоскалить на половину хороших библиотек в докуменации которых это упорно путают. И тем не менее: консольная программа декларирует параметры командной строки (часто это мини язык) — но парсит она аргументы.
как мы видим на этом куске кода C++ и C# отрогональны друг другу. так в C# лямбда захватывает все что находится в контексте.
Т.t. замкнуть на самом деле значение переменной в в объявлении сишарповой лямбды( анонимной функции ) просто невозможно.
string name = "Alice";
var closure = Closure(name);
var lambda = new Lamda(delegate { WriteLine($"Hello, {name}!"); });
lambda();
closure();
name = "Bob";
lambda();
closure();
Action Closure(string name) => delegate { WriteLine($"Hello, {name}!"); };
public delegate void Lamda();
C++ же в конструкте анонимной функции наворотил еще больше. что проще для понимания хз. ну в плюсах это хотя бы явно.
Как тут уже замечали MS любит придумывать собственную терминологию, что вносит дополнительную путаницу в смыслы той или иной техники.
vaa>как мы видим на этом куске кода C++ и C# отрогональны друг другу. так в C# лямбда захватывает все что находится в контексте. vaa>Т.t. замкнуть на самом деле значение переменной в в объявлении сишарповой лямбды( анонимной функции ) просто невозможно.