Лямбда-выражение в list.erase - PullRequest
0 голосов
/ 16 сентября 2018

Я изучаю лямбды и пробую различные примеры, и я не уверен, что понимаю, почему это не работает:

std::list<int> listIntegers;
listIntegers.push_back(40);

listIntegers.erase([listIntegers]() {
return std::find(listIntegers.begin(), listIntegers.end(), 40);
});

Я тоже пытался писать явно:

listIntegers.erase([listIntegers]()->std::list<int>::const_iterator {
    return std::find(listIntegers.begin(), listIntegers.end(), 40);
    });

Однако это, конечно, работает:

auto found40Iterator = std::find(listIntegers.begin(), listIntegers.end(), 40)

listIntegers.erase(found40Iterator);

1 Ответ

0 голосов
/ 17 сентября 2018

Рассмотрим случай

listIntegers.erase([listIntegers]()->std::list<int>::const_iterator {
    return std::find(listIntegers.begin(), listIntegers.end(), 40);
});

Список захвата [listIntegers] принимает значение listInteger, копия сохраняется с лямбда-выражением.Поскольку listIntegers, используемый в std::find(listIntegers.begin(), listIntegers.end(), 40), является копией оригинала, он найдет элемент 40 в этой копии.Поэтому возвращаемый итератор является итератором для элемента в копии, а не в оригинале.Итераторы могут использоваться только с другими итераторами из того же контейнера и с их исходным контейнером.Проблема, которую вы видите, состоит в том, что возвращаемое значение вашей лямбды используется с listIntegers, но на самом деле оно относится к элементу в другом диапазоне (копия listIntegers).

Решение состоит в том, чтобы изменить список захвата на listIntegers по ссылке, чтобы он работал на исходном listIntegers.Существует также проблема, заключающаяся в том, что вы не вызываете лямбду, а вместо этого передаете ее в качестве аргумента erase.Добавив пару скобок, мы можем вместо этого вызвать лямбду и передать ее возвращаемое значение в erase.

listIntegers.erase([&listIntegers]() {
    // Added & here ^
    return std::find(listIntegers.begin(), listIntegers.end(), 40);
}());
//^ Call the lambda
...