Подтвердить что ты не робот

Кэширование конечного итератора - хорошая идея или плохая идея?

Вообще говоря, хорошо ли кэшировать конечный итератор (в частности, контейнеры STL) для эффективности и скорости? например, в следующем бите кода:

std::vector<int> vint;
const std::vector<int>::const_iterator end = vint.end();
std::vector<int>::iterator it = vint.begin();

while (it != end)
{
   ....
   ++it;
}

В каких условиях конечное значение будет признано недействительным? стирает с конца контейнера, чтобы быть недействительным в контейнерах STL all или только некоторые?

4b9b3361

Ответ 1

В простом случае vector итератор end будет меняться при добавлении или удалении элементов из контейнера; хотя, как правило, безопаснее предполагать, что если вы мутируете контейнер во время итерации по нему, итераторы all становятся недействительными. Итераторы могут быть реализованы по-разному в любой заданной STL-реализации.

Что касается кэширования итератора end - это, безусловно, актуально для его кеширования, но чтобы узнать, действительно ли это на самом деле быстрее в вашем случае, лучше всего для вас профилировать свой код и видеть. Хотя получение итератора end из vector, скорее всего, является быстрой реализацией с недавней библиотекой и компилятором STL, я работал над прошлыми проектами, где кеширование итератора end дало нам значительный прирост скорости. (Это было на PlayStation 2, так что возьмите с солью.)

Ответ 2

Если говорить об эффективности и скорости: кеширование конечного итератора не нужно из-за оптимизации компилятора и вложения.

Ответ 3

Удаление из контейнера, поверх которого вы сейчас выполняете итерацию, всегда плохая идея. Фактическое кэширование конечного итератора не изменит этого.

ч.

Ответ 4

Вообще говоря, это хорошая идея для кэширования конечного итератора (в частности STL) для эффективности и скорости?

Если вы используете алгоритмы контейнера STL, кеширование конечного итератора происходит в любом случае (поскольку вы передаете результат container.end() в качестве параметра).

Если вы изменяете память контейнера (вставляете/удаляете элементы), это плохая плохая идея.

Кроме того, кеширование эффективности редко имеет смысл: в большинстве случаев end() встроен компилятором, а когда это не так, очень вероятно, что ваша эффективность не зависает в конце() результате, кэшируются. YMMV, хотя.

Ответ 5

Правила недействительности (для итераторов) определены очень явно для каждого типа контейнера. Я нахожу сайт SGI очень полезным http://www.sgi.com/tech/stl/table_of_contents.html

В частности, для векторов я нахожу:

[5] Итераторы векторов недействительны при перераспределении памяти. Кроме того, вставка или удаление элемента в середине вектора аннулирует все итераторы, указывающие на элементы, следующие за точкой вставки или удаления. Из этого следует, что вы можете не допускать, чтобы векторные итераторы были недействительными, если вы используете reserve() для предопределения столько памяти, сколько будет использоваться вектором, и если все вставки и удаления находятся на конце вектора.

Ответ 6

Я часто использую этот стиль для итерации контейнеров:

// typedef std::vector<Person> Persons;
Persons::iterator it = persons.begin(), end = persons.end();
for (; it != end; ++it)
{
    Person & person = *it;
    // ...
}

Удаление элемента из вектора отменяет все итераторы после удаленной позиции.

Я не уверен в других типах контейнеров. В любом случае, я думаю, было бы безопасно предположить, что все итераторы становятся недействительными после стирания. Если вам действительно нужна очень конкретная информация, вы всегда можете ее найти. Мне редко это нужно, потому что из-за моего довольно консервативного стиля кодирования.

Ответ 7

В общем случае не имеет значения, следует ли кэшировать конечный итератор. Если вы считаете, что это имеет значение, тогда вы уже должны использовать профилировщик своего кода и сможете обрабатывать оба варианта. Я подозреваю, что он может отличаться в зависимости от типа контейнера, но профилировщик будет единственным способом узнать наверняка с учетом вашего компилятора, оптимизаций и поставщика STL.

Ответ 8

Это действительно зависит от того, что вы делаете в ... коде.

Если компилятор может доказать, что vint.end() не изменится, то это может не иметь значения, но вы во власти оптимизаций компилятора, и насколько ясен ... код.

Ваш подход больше всего помогает компилятору, вы обещаете, что end итератор не будет признан недействительным и что вы не собираетесь изменять элемент в end (что в любом случае недопустимо). Вы не можете быть более ясным, чем это, о том, что вы обещаете сделать в ...

Это 2019 год, и циклы for-range в основном эквивалентны вашему коду: https://en.cppreference.com/w/cpp/language/range-for

{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {

        range_declaration = *__begin;
        loop_statement

    }
}

что, кстати, означает, что если вы на самом деле не заинтересованы в it, но в *it, вы можете положить конец (не каламбур) дилемме и просто написать:

std::vector<int> vint;
for(auto&& e : vint)
{
   .... // use 'e' instead of '*it'.
}