Можем ли мы увеличить итератор на несколько позиций без функции «продвижения»?

Я знаю, что мы можем использовать функцию advance() для увеличения итератора. Мы также используем iterator++ для увеличения итератора на одну позицию. Почему мы не можем использовать it+=2?

int main()
{
    list<int> l1{1, 2, 3, 5, 6};
    list<int> l2{2, 6, 8};
    auto it = l1.begin();
    advance(it, 2);         //works
    it++;                   //works
    // it+=2;                  //not work
    l2.splice(l2.begin(), l1, it);

    for(int a: l2) cout<<a<<" ";
    cout<<endl;

    return 0;
}

Вы можете запустить указанный выше код здесь.

Список имеет двунаправленный итератор, а не итератор с произвольным доступом. advance использует механизм времени компиляции для оптимизации дополнительных операций для различных типов итераторов.

William Lee 26.10.2018 08:17

Итак, ваш вопрос: "почему operator+=() не был указан как синтаксический сахар вокруг std::advance()?" или это действительно "Почему я не могу использовать здесь operator+=()?"

moooeeeep 26.10.2018 08:44
16
2
2 095
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

operator += поддерживается только RandomAccessIterator; обратите внимание, что он должен иметь постоянную сложность.

Итератором std::list является BidirectionalIterator, который не поддерживает operator +=. (Итератором std::vector и std::array является RandomAccessIterator.)

Обратите внимание, что оба они могут использоваться с std :: advance, при использовании для RandomAccessIterator сложность постоянна; при использовании для других InputIterator (включая BidirectionalIterator) сложность линейна. Это означает, что использование std::advance - хорошая идея, поскольку он более общий и может автоматически использовать преимущества RandomAccessIterator.

Будет ли std::advance хорошей идеей, зависит от гарантии сложности, которую вы желаете для своего алгоритма. Использование += гарантирует, что если кто-то случайно вызовет ваш алгоритм с не-RandomAccessIterator, он получит ошибку времени компиляции вместо мучительно медленного алгоритма. Например, если std::sort использовал std::advance, вы могли бы вызвать его на std::list ...

Matthieu M. 26.10.2018 15:08

Если вы хотите обеспечить сложность времени компиляции на основе IteratorCategory, сделайте это с помощью static_assert, а не с каким-либо синтаксисом, который не работает со слишком слабым итератором ...

MFH 26.10.2018 20:57

Почему здесь все еще действует оператор ++?

Lusha Li 27.10.2018 00:47

@LushaLi Поддерживается всеми InputIterators, включая BidirectionalIterator и RandomAccessIterator. И посмотрите комментарий PeteBecker, это может помочь вам понять, почему итераторы спроектированы таким образом.

songyuanyao 27.10.2018 12:53

Вы не можете использовать += 2 с этим итератором, потому что в общем случае увеличение итератора std::list<> на произвольное значение является относительно операцией неэффективный. += не определен для вашего типа итератора специально, чтобы вы не могли по неосторожности или неосознанно использовать эту неэффективную операцию в своем коде. Вместо этого, если вы действительно хотите это сделать, вы должны использовать std::advance, функцию «красного флага», предназначенную для того, чтобы подчеркнуть тот факт, что вы, вероятно, делаете что-то неэффективное. std::advance в основном предназначен для создания набросков кода или для резервного кода маловероятный казнь. Вы не должны безвозмездно использовать std::advance в производственном коде. Если вы вдруг обнаружите, что полагаетесь на std::advance, это означает, что вам, вероятно, нужно изменить структуру данных. По сути, std::advance похож на слепок - избегайте его, если у вас нет очень-очень-очень веской причины для его использования.

В качестве альтернативы можно использовать std::next

it = std::next(it, 2);

Эта функция намного проще в использовании, чем std::advance. По умолчанию эта функция предназначена для продвижения итератора на 1 шаг вперед, но вы можете указать второй параметр, чтобы переместить его дальше. Опять же, когда значение второго параметра непостоянно и потенциально велико, его следует рассматривать как функцию «красного флажка». Постоянное значение 2 определенно находится в допустимых пределах.

Причина, по которой += связан с операцией с постоянным временем, возможно, состоит в том, что указатели обычно является первым типом итератора, который вводится, и, естественно, поддерживает арифметику указателей из своего наследия c - просто аккуратно, чтобы понять, что этот дизайн интерфейса был более или менее принудительным на C++ существующей культурой.

WorldSEnder 26.10.2018 10:44

@WorldSEnder - += связан с операцией с постоянным временем, потому что именно так была разработана STL (Стандартная библиотека шаблонов): операции итератора выполняются с постоянным временем. Период. Такие вещи, как std::advance и std::next, были добавлены ревизионистами, которые не понимали элегантности оригинальной формулировки. <g> А если серьезно: это удобства, и они названы функциями, а не операторами, чтобы было понятно, что они могут быть дорогими.

Pete Becker 26.10.2018 14:53

Другие вопросы по теме