std :: list и std :: for_each: где мой конец?

Рассмотрим следующий минимальный пример:

#include <functional>
#include <algorithm>
#include <list>

int main() {
    std::list<std::function<void()>> list;
    list.push_back([&list](){ list.push_back([](){ throw; }); });
    std::for_each(list.cbegin(), list.cend(), [](auto &&f) { f(); });
}

Он компилирует и выдает исключение во время выполнения.
Я предположил, что std::for_each выполняется только первая лямбда, но, по-видимому, я ошибся: если я добавлю еще одну лямбду в конце список, итерация достигает и этой лямбды.

Давайте вернемся к примеру (push_front вместо push_back и crbegin /crend вместо cbegin /cend):

#include <functional>
#include <algorithm>
#include <list>

int main() {
    std::list<std::function<void()>> list;
    list.push_front([&list](){ list.push_front([](){ throw; }); });
    std::for_each(list.crbegin(), list.crend(), [](auto &&f) { f(); });
}

Из-за предыдущего примера я ожидал, что это скомпилируется и завершится сбоем.
Вместо этого он компилируется и не падает. На этот раз функция, помещенная в начало списка, не выполняется.

Вопрос довольно прост: это правильно?
Почему два примера настолько противоречивы?

В первом случае я ожидал чего-то другого, и я ошибся, это не проблема.
Во всяком случае, я бы ожидал согласованности между двумя циклами. Я имею в виду, что вторая функция выполняется в одном случае, а в другом - нет, но в обоих случаях я выполняю итерацию от begin до end .
Что не так в моих рассуждениях?

7 голосов | спросил skypjack 21 12016vEurope/Moscow11bEurope/MoscowMon, 21 Nov 2016 16:27:37 +0300 2016, 16:27:37

1 ответ


0

Если честно, результаты, которые ты получаешь, кажутся такими, как я ожидал. Давайте рассмотрим первый пример:

1.

list.push_back([&list](){ list.push_back([](){ throw; }); });

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
v
[lambda]----[end]

2. начать итерацию по списку

Итерация 1:

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
v
[lambda]----[end]
^
+-- current

f() вызывает list.push_back([](){ throw; });

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
v
[lambda]----[inner_lambda]----[end]
^
+-- current

Итерация 2: (++current)

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
v
[lambda]----[inner_lambda]----[end]
            ^
            +-- current

f() вызывает throw;

конец



Теперь давайте сделаем в другом направлении.

Прежде всего, посмотрите, как на самом деле представлены обратные итераторы - это важно (изображение из cppreference):  обратные итераторы

Важная часть: обратные конечные точки к нормальному началу. Но проблема в том, что со списком одна может вставить что-то перед begin, но это невозможно после ---- +: = 11 =: + ----. Этот инвариант нарушается обратными итераторами.

1.

end

Состояние списка:

list.push_front([&list](){ list.push_front([](){ throw; }); });

2. начать итерацию по списку

Итерация 1:

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
|
|+-- list.rend().base()
||
||          +-- list.rbegin().base()
vv          v
[lambda]----[end]

+-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend() приводит к *current.

[lambda] вызывает f()

Состояние списка:

list.push_front([](){ throw; });

Обратите внимание, что переданный +-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [inner_lambda]----[lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend().base() не изменился - но он больше не указывает на первый (после последнего обращенного) элемент .

Итерация 2: (list.rend().base())

++current

+-- list.begin() (not necessarily what has been passed to for_each) | |+-- list.rend().base() || || +-- list.rbegin().base() vv v [inner_lambda]----[lambda]----[end] ^ ^ | +---- current | +--------- passed list.rend().base() == current

конец



Теперь давайте попробуем другой вариант , по моей ошибке эта часть важна для перебора итераций по списку :

1.

passed list.rend().base()

Состояние списка:

list.push_front([&list](){ list.push_front([](){ throw; }); });

2. начать итерацию по списку

Итерация 1:

Состояние списка:

+-- list.begin() (not necessarily what has been passed to for_each)
v
[lambda]----[end]

+-- list.begin() (not necessarily what has been passed to for_each) v [lambda]----[end] ^ +-- current вызывает f()

Итератор для текущего не считается недействительным и /или не должен указывать куда-либо еще, чем он уже указывал.

Состояние списка:

list.push_front([](){ throw; });

Итерация 2: (+-- list.begin() (not necessarily what has been passed to for_each) v [inner_lambda]----[lambda]----[end] ^ +-- current )

Состояние списка:

++current

конец

ответил krzaq 21 12016vEurope/Moscow11bEurope/MoscowMon, 21 Nov 2016 16:56:34 +0300 2016, 16:56:34

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132