Сегодня я столкнулся с очень не интуитивным поведением (по крайней мере для меня) в лямбдах C ++ 11. Код, о котором идет речь, следующий:
#include <stdio.h>
auto sum(int x) {
return [&x](int y) {
return x + y;
};
}
int main() {
int a = sum(2)(3);
printf("%d\n",a);
}
Вместо печати 5, это печатает бред. На самом деле, по крайней мере, в моей версии GCC, если я включаю флаг оптимизации -O2, он на самом деле печатает 5. Поскольку выходные данные зависят от уровня оптимизации компилятора, это неопределенное поведение. Через некоторое время, я думаю, я понял, что происходит.
Когда вызывается функция sum, переменная стека, соответствующая аргументу x, устанавливается в 2, тогда функция sum возвращается, и эта переменная стека может быть перезаписана всем, что нужно поместить компилятору для выполнения следующего кода, и время, когда лямбда в конечном итоге исполняется, место, где x больше не содержит 2, и программа добавляет 3 к произвольному целому числу.
Есть ли какой-нибудь элегантный способ сделать карри в C ++, гарантирующий, что переменная будет перехвачена правильно?
int x
имеет ограниченный срок службы. Ссылки на переменные автоматического хранения (то, что вы называете «стеком») действительны только в течение времени жизни переменной. В этом случае только до конца стекового фрейма (области видимости), где существует переменная, или функции для аргументов функции.
[&]
захватывает любую упомянутую («локальную») переменную по ссылке, кроме this
(который фиксируется значением, если используется или используется неявно). [=]
захватывает любую упомянутую переменную по значению. [x]
будет захватывать x
явно, и [&x]
по ссылке явно. В C ++ 17 [*this]
тоже работает.
Существует также [x=std::move(x)]
, или же [blah=expression]
,
В общем, если лямбда переживет текущую область, не используйте [&]
: будьте откровенны о том, что вы захватываете.
Других решений пока нет …