Каково время жизни объектов, захваченных ссылкой в ​​асинхронной функции на C ++? [Дубликат]

Какой адрес имел бы me в противном случае? Здесь вы указали ему адрес tmp - но если вы замените его на int ** me = & amp; aa.get_dummy (); , где он будет указывать? [ ! d5]

Нет никакого значимого ответа на этот вопрос, поэтому стандарт требует, чтобы аргумент & amp; был lvalue.

10
задан Ashish Negi 5 January 2015 в 08:42
поделиться

3 ответа

Да, это вызывает неопределенное поведение. 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;
}

( Посмотрите, как он работает .)

20
ответ дан cdhowie 15 August 2018 в 21:48
поделиться

К сожалению, C ++ lambdas могут захватывать по ссылке, но не решают проблему « вверх по funarg ».

Для этого потребуется выделить захваченные локали в ячейках и сборку мусора или подсчет ссылок для освобождения. C ++ не делает этого, и, к сожалению, это делает C ++ lambdas намного менее полезным и более опасным, чем на других языках, таких как Lisp, Python или Javascript.

Более конкретно, в моем опыте вам следует избегать во что бы то ни стало скрытого захвата (т. е. используя форму [&](…){…}) для лямбда-объектов, которые выживают в локальной области, потому что это рецепт случайных segfaults позже во время обслуживания.

Всегда тщательно планируйте, что делать, а также о том, как и как захваченные ссылки.

Конечно, безопасно записывать все по ссылке с помощью [&], если все, что вы делаете, это просто использовать лямбда в той же области, чтобы передавать код, например, таким алгоритмам, как std::sort, не имея для определения именованной функции компаратора вне функции.

Подход, который может работать иногда, записывается по значению a shared_ptr в состояние, выделенное кучей. Это в основном реализует вручную то, что делает Python автоматически (но обратите внимание на циклы ссылок, чтобы избежать утечек памяти: у Python есть сборщик мусора, C ++ - нет).

8
ответ дан 6502 15 August 2018 в 21:48
поделиться
  • 1
    [&] является прекрасным, если срок действия замыкания и его копии ограничивается прилагаемым {}, иначе он небезопасен или опасен. Поскольку такие ограниченные сроки закрытия жизни являются общими, избегать его, как правило, кажется излишним? – Yakk - Adam Nevraumont 5 January 2015 в 09:34
  • 2
    @Yakk: Я думал о лямбдах для их использования в качестве закрытий, а не как о бедных способ встраивания кода в алгоритмы. Я отредактирую, чтобы это объяснить. – 6502 5 January 2015 в 09:38
  • 3
    Лучший ответ для указания на проблему funarg и для сравнения с другими языками. – Maggyero 30 January 2017 в 08:09
  • 4
    Я не думаю, что это хороший совет сказать, что люди должны избегать захвата по ссылке любой ценой. Вы должны это делать, когда это хорошая идея, которая довольно часто, по моему опыту. Очевидно, что значение должно оставаться в области действия на протяжении всего времени выполнения лямбда. – Dan Nissenbaum 16 July 2018 в 21:36
  • 5
    @DanNissenbaum: Я сказал, что вам следует избегать захвата по умолчанию по ссылке , если лямбда выживет в области , потому что это рецепт катастрофы. Захват по ссылке по умолчанию с [&] отлично подходит для только для 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();
    })
}
0
ответ дан Nikolay Mihaylov 15 August 2018 в 21:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: