№
Какой адрес имел бы me
в противном случае? Здесь вы указали ему адрес tmp
- но если вы замените его на int ** me = & amp; aa.get_dummy ();
, где он будет указывать? [ ! d5]
Нет никакого значимого ответа на этот вопрос, поэтому стандарт требует, чтобы аргумент & amp;
был lvalue.
Да, это вызывает неопределенное поведение. Lambdas будет ссылаться на объекты, расположенные в стеке, которые вышли за рамки. (Технически, как я понимаю, поведение определяется до доступа lambdas a
и / или b
. Если вы никогда не вызываете возвращенные lambdas, тогда нет UB.)
Это неопределенное поведение так же, как неопределенное поведение, чтобы вернуть ссылку на локализованный стек, а затем использовать эту ссылку после локального выхода из области видимости, за исключением того, что в этом случае она немного запутывается lambda.
Кроме того, обратите внимание, что порядок, в котором вызывается lambdas, не указан - компилятор может вызывать f.second()
перед f.first()
, потому что оба являются частью одного и того же полного выражения. Поэтому, даже если мы исправим неопределенное поведение, вызванное использованием ссылок на уничтоженные объекты, как 2 0
, так и 2 1
являются еще действительными выходами из этой программы, и которые вы получаете, зависит от порядка, в котором ваш компилятор решает выполнить лямбды. Обратите внимание, что это не неопределенное поведение , потому что компилятор вообще не может делать вообще , а просто имеет некоторую свободу в решении порядка некоторые вещи .
(Имейте в виду, что <<
в вашей функции main()
вызывает пользовательскую функцию operator<<
, а порядок, в котором вычисляются аргументы функции, не указан. могут испускать код, который оценивает все аргументы функции в одном и том же полном выражении в любом порядке, с ограничением, что все аргументы функции должны быть оценены до того, как эта функция будет вызвана.)
Исправить первая проблема, используйте std::shared_ptr
для создания объекта с подсчетом ссылок. Захватите этот общий указатель по значению, и lambdas сохранит объект, указывающий на объект, до тех пор, пока они (и любые их копии) существуют. Этот выделенный кучей объект - это где мы будем хранить общее состояние a
и b
.
Чтобы исправить вторую проблему, оцените каждую лямбда в отдельном заявлении.
Вот ваш код, переписанный с неопределенным поведением, и с f.first()
гарантированно вызывается перед f.second()
:
std::pair<std::function<int()>, std::function<int()>> addSome() {
// We store the "a" and "b" ints instead in a shared_ptr containing a pair.
auto numbers = std::make_shared<std::pair<int, int>>(0, 0);
// a becomes numbers->first
// b becomes numbers->second
// And we capture the shared_ptr by value.
return std::make_pair(
[numbers] {
++numbers->first;
++numbers->second;
return numbers->first + numbers->second;
},
[numbers] {
return numbers->first;
}
);
}
int main() {
auto f = addSome();
// We break apart the output into two statements to guarantee that f.first()
// is evaluated prior to f.second().
std::cout << f.first();
std::cout << " " << f.second();
return 0;
}
К сожалению, C ++ lambdas могут захватывать по ссылке, но не решают проблему « вверх по funarg ».
Для этого потребуется выделить захваченные локали в ячейках и сборку мусора или подсчет ссылок для освобождения. C ++ не делает этого, и, к сожалению, это делает C ++ lambdas намного менее полезным и более опасным, чем на других языках, таких как Lisp, Python или Javascript.
Более конкретно, в моем опыте вам следует избегать во что бы то ни стало скрытого захвата (т. е. используя форму [&](…){…}
) для лямбда-объектов, которые выживают в локальной области, потому что это рецепт случайных segfaults позже во время обслуживания.
Всегда тщательно планируйте, что делать, а также о том, как и как захваченные ссылки.
Конечно, безопасно записывать все по ссылке с помощью [&]
, если все, что вы делаете, это просто использовать лямбда в той же области, чтобы передавать код, например, таким алгоритмам, как std::sort
, не имея для определения именованной функции компаратора вне функции.
Подход, который может работать иногда, записывается по значению a shared_ptr
в состояние, выделенное кучей. Это в основном реализует вручную то, что делает Python автоматически (но обратите внимание на циклы ссылок, чтобы избежать утечек памяти: у Python есть сборщик мусора, C ++ - нет).
[&]
является прекрасным, если срок действия замыкания и его копии ограничивается прилагаемым {}
, иначе он небезопасен или опасен. Поскольку такие ограниченные сроки закрытия жизни являются общими, избегать его, как правило, кажется излишним?
– Yakk - Adam Nevraumont
5 January 2015 в 09:34
[&]
отлично подходит для только для lambdas, которые используются немедленно .
– 6502
16 July 2018 в 22:01
Когда вы выходите из сферы действия. Это сделает копию ваших местных жителей.
void func(void)
{
auto mylocal = getFromSomeWhere();
doSearch([=] {
mylocal->foudnSomthing();
});
}
Когда вы находитесь в области действия, лучше использовать ссылки
MyType func(void)
{
auto mylocal = getFromSomeWhere();
return ([&] {
return mylocal->doOperation();
})
}