THISNOTES Telegram 191
#cpp

Лямбды 1/2.

1. Все мы помним, что список захвата работает только для локальных перенных. В то время как статические и глобальные переменные захватываются, грубо говоря, автоматически. Такое верно и для constexpr переменных, потому что в итоге у вас в коде будет подставлено конкретное значение. Логично и просто.
И тут стоит помнить, что константные интегральные типы неявно являются constexpr:

const int i = 1; // implicitly constexpr
const std::size_t j = 1ull; // implicitly constexpr
const float f = 1.f; // not implicitly constexpr


Т.е. первые две переменные мы можем не захватывать при использовании в лямбде, тогда как третью необходимо.

2. Immediately Invoked Function Expressions (IIFE).
Это довольно полезный подход использования лямбд, когда они вызываются сразу же:

[]{ std::cout << “ASD”; }();

Конечно в таком виде такое не используется. Гораздо более полезным такое будет, когда хочется инициализировать объект каким-то нетривиальным способом:

A a;
if (condition) {
a = firstWay();
} else {
a = secondWay();
}


Мы разделили инициализацию объекта на две части. Между ними он может быть невалидным, и использование до ифа может привести к уб. Как-то небезопасно и не оч поддерживаемо.
А ещё класс может не иметь конструктора по умолчанию, что в целом делает подход выше невозможным.
А ещё инициализируемая переменная может быть помечена const, что так же не даёт инициализировать её в ифе (в отличие от const в C++, в Java ключевое слово final, которое говорит, что переменной можно присвоить что-то только единожды -> код выше в случае final переменной заработает).

Если код достаточно простой, можно заюзать тернарный оператор:

const A a = condition ? firstWay() : secondWay();

Но иногда этого может быть недостаточно.
Можно вынести код инициализации в функцию, но, опуская технические недостатки, мы таким образом разделяем логику функции, из-за чего её теперь нельзя просто читать сверху вниз без посторонних перемещений по коду.
Потому лямбда тут довольно хороший выбор:

const A a = [condition] {
if (condition) {
return firstWay();
} else {
return secondWay();
}
}();


Такой подход хорошо подходит для всех случаев, когда у вас действия происходят рядом с perfect forwarding:

std::vector<A> v;
v.emplace_back([condition] {
if (condition) {
return firstWay();
} else {
return secondWay();
}
}());


и другими функциями того же рода.

Есть поинт, что две скобки вызова лямбды в конце могут быть легко просмотрены (и можно подумать, что это просто лямбда, а не результат её выполнения), потому можно использовать std::invoke, но кмк тащить хедер ради такого не оч хорошо (только если он у вас уже был).

3. Вот тут (пункт 3) писал про приведение лямбд с пустым списком захвата к указателю на функцию.
Интересно, что такой хак работает не всегда:

auto* fptr = +[](auto i) {
return i * i;
};


Что в целом понятно и ожидаемо.

4. Вот тут таска (и чуть ниже ответ) про то, как сообразить call_once и call_n с помощью лямбд.
Кстати прикольно, что на нескольких различных докладах на CppCon чуваки говорили, что на ручных бенчмарках лямбда, инициализирующая статический объект, — более эффективна как реализация std::call_once чем собственно реализации в версиях стандартной библиотеки.

5. С появлением generic лямбд с C++14 вы можете писать примерно все обычные для шаблонов штуки вроде auto&& для универсальных ссылок или даже auto… для variadic templates. Ну и лямбды друг в друга можно передавать. Удобно.

6. Variable template lambda.
С C++14 можно создавать шаблонные переменные. И, что интересно, если вы используете лямбду для инициализации такой переменной, вы имеете доступ к шаблонному аргументу переменной:

template <typename T>
constexpr auto c_cast = [](auto x) {
return (T)x;
};


Тут, грубо говоря, вы получаете не просто шаблонный operator() в вашей лямбде, но и сам класс лямбды становится шаблонным.
5👍10🔥31



tgoop.com/thisnotes/191
Create:
Last Update:

#cpp

Лямбды 1/2.

1. Все мы помним, что список захвата работает только для локальных перенных. В то время как статические и глобальные переменные захватываются, грубо говоря, автоматически. Такое верно и для constexpr переменных, потому что в итоге у вас в коде будет подставлено конкретное значение. Логично и просто.
И тут стоит помнить, что константные интегральные типы неявно являются constexpr:

const int i = 1; // implicitly constexpr
const std::size_t j = 1ull; // implicitly constexpr
const float f = 1.f; // not implicitly constexpr


Т.е. первые две переменные мы можем не захватывать при использовании в лямбде, тогда как третью необходимо.

2. Immediately Invoked Function Expressions (IIFE).
Это довольно полезный подход использования лямбд, когда они вызываются сразу же:

[]{ std::cout << “ASD”; }();

Конечно в таком виде такое не используется. Гораздо более полезным такое будет, когда хочется инициализировать объект каким-то нетривиальным способом:

A a;
if (condition) {
a = firstWay();
} else {
a = secondWay();
}


Мы разделили инициализацию объекта на две части. Между ними он может быть невалидным, и использование до ифа может привести к уб. Как-то небезопасно и не оч поддерживаемо.
А ещё класс может не иметь конструктора по умолчанию, что в целом делает подход выше невозможным.
А ещё инициализируемая переменная может быть помечена const, что так же не даёт инициализировать её в ифе (в отличие от const в C++, в Java ключевое слово final, которое говорит, что переменной можно присвоить что-то только единожды -> код выше в случае final переменной заработает).

Если код достаточно простой, можно заюзать тернарный оператор:

const A a = condition ? firstWay() : secondWay();

Но иногда этого может быть недостаточно.
Можно вынести код инициализации в функцию, но, опуская технические недостатки, мы таким образом разделяем логику функции, из-за чего её теперь нельзя просто читать сверху вниз без посторонних перемещений по коду.
Потому лямбда тут довольно хороший выбор:

const A a = [condition] {
if (condition) {
return firstWay();
} else {
return secondWay();
}
}();


Такой подход хорошо подходит для всех случаев, когда у вас действия происходят рядом с perfect forwarding:

std::vector<A> v;
v.emplace_back([condition] {
if (condition) {
return firstWay();
} else {
return secondWay();
}
}());


и другими функциями того же рода.

Есть поинт, что две скобки вызова лямбды в конце могут быть легко просмотрены (и можно подумать, что это просто лямбда, а не результат её выполнения), потому можно использовать std::invoke, но кмк тащить хедер ради такого не оч хорошо (только если он у вас уже был).

3. Вот тут (пункт 3) писал про приведение лямбд с пустым списком захвата к указателю на функцию.
Интересно, что такой хак работает не всегда:

auto* fptr = +[](auto i) {
return i * i;
};


Что в целом понятно и ожидаемо.

4. Вот тут таска (и чуть ниже ответ) про то, как сообразить call_once и call_n с помощью лямбд.
Кстати прикольно, что на нескольких различных докладах на CppCon чуваки говорили, что на ручных бенчмарках лямбда, инициализирующая статический объект, — более эффективна как реализация std::call_once чем собственно реализации в версиях стандартной библиотеки.

5. С появлением generic лямбд с C++14 вы можете писать примерно все обычные для шаблонов штуки вроде auto&& для универсальных ссылок или даже auto… для variadic templates. Ну и лямбды друг в друга можно передавать. Удобно.

6. Variable template lambda.
С C++14 можно создавать шаблонные переменные. И, что интересно, если вы используете лямбду для инициализации такой переменной, вы имеете доступ к шаблонному аргументу переменной:

template <typename T>
constexpr auto c_cast = [](auto x) {
return (T)x;
};


Тут, грубо говоря, вы получаете не просто шаблонный operator() в вашей лямбде, но и сам класс лямбды становится шаблонным.

BY this->notes.


Share with your friend now:
tgoop.com/thisnotes/191

View MORE
Open in Telegram


Telegram News

Date: |

The best encrypted messaging apps Deputy District Judge Peter Hui sentenced computer technician Ng Man-ho on Thursday, a month after the 27-year-old, who ran a Telegram group called SUCK Channel, was found guilty of seven charges of conspiring to incite others to commit illegal acts during the 2019 extradition bill protests and subsequent months. Telegram desktop app: In the upper left corner, click the Menu icon (the one with three lines). Select “New Channel” from the drop-down menu. A new window will come up. Enter your channel name and bio. (See the character limits above.) Click “Create.” As five out of seven counts were serious, Hui sentenced Ng to six years and six months in jail.
from us


Telegram this->notes.
FROM American