Каков наиболее эффективный способ получить индекс итератора std::vector?

Я повторяю вектор и нуждаюсь в индексе, на который указывает итератор. AFAIK это можно сделать двумя способами:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

Каковы плюсы и минусы этих методов?

Ответ 1

Я бы предпочел it - vec.begin() именно по той противоположной причине, которую дал Naveen: поэтому он не будет компилироваться, если вы измените вектор на список. Если вы делаете это на каждой итерации, вы можете легко превратить алгоритм O (n) в алгоритм O (n ^ 2).

Другой вариант, если вы не прыгаете в контейнере во время итерации, будет поддерживать индекс как второй счетчик циклов.

Примечание: it общее имя для итератора контейнера, std::container_type::iterator it; ,

Ответ 2

Я бы предпочел std::distance(vec.begin(), it), так как это позволит мне изменить контейнер без каких-либо изменений кода. Например, если вы решили использовать std::list вместо std::vector, который не предоставляет итератор с произвольным доступом, ваш код все равно будет компилироваться. Поскольку std:: distance выбирает оптимальный метод в зависимости от характеристик итератора, вы также не будете иметь ухудшения производительности.

Ответ 3

Как показали UncleBens и Naveen, для обоих есть веские причины. Какой из них "лучше" зависит от того, какое поведение вы хотите: хотите ли вы гарантировать постоянное поведение или хотите ли вы вернуться к линейному времени, когда это необходимо?

it - vec.begin() принимает постоянное время, но operator - определяется только для итераторов произвольного доступа, поэтому код вообще не будет компилироваться с итераторами списков.

std::distance(vec.begin(), it) работает для всех типов итераторов, но будет выполняться только операция с постоянным временем, если используется для итераторов произвольного доступа.

Ни один из них не "лучше". Используйте тот, который делает то, что вам нужно.

Ответ 4

Мне нравится этот: it - vec.begin(), потому что для меня это четко говорит "расстояние от начала". С итераторами мы привыкли думать в терминах арифметики, поэтому знак - является самым ярким индикатором здесь.

Ответ 5

Если вы уже ограничены/жестко закодированы алгоритмом использования только std::vector::iterator и std::vector::iterator, на самом деле не имеет значения, какой метод вы будете использовать. Ваш алгоритм уже конкретизирован за пределами того места, где выбор одного из других может иметь какое-либо значение. Они оба делают то же самое. Это только вопрос личных предпочтений. Я лично использовал бы явное вычитание.

Если, с другой стороны, вы хотите сохранить более высокую степень общности в своем алгоритме, а именно, чтобы позволить возможность, что когда-нибудь в будущем она может быть применена к другому типу итератора, тогда лучший способ зависит по вашим намерениям. Это зависит от того, насколько ограничительным вы хотите быть в отношении типа итератора, который можно использовать здесь.

  • Если вы используете явное вычитание, ваш алгоритм будет ограничен довольно узким классом итераторов: итераторы с произвольным доступом. (Это то, что вы получаете от std::vector)

  • Если вы используете distance, ваш алгоритм будет поддерживать гораздо более широкий класс итераторов: входные итераторы.

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

Ответ 6

В соответствии с http://www.cplusplus.com/reference/std/iterator/distance/, поскольку vec.begin() является итератором произвольного доступа, метод расстояния использует оператор -.

Итак, ответ с точки зрения производительности один и тот же, но, возможно, использование distance() проще понять, если кто-то должен будет читать и понимать ваш код.

Ответ 7

Я бы использовал вариант - только для std::vector - довольно ясно, что имеется в виду, а простота операции (которая не превышает вычитание указателя) выражается синтаксисом (, с другой стороны, звучит как пифагор в первом чтении, не так ли?). Как указывает UncleBen, - также действует как статическое утверждение в случае, когда vector случайно изменено на list.

Кроме того, я думаю, что это гораздо более распространено - не имеют числа, чтобы доказать это. Главный аргумент: it - vec.begin() короче в исходном коде - меньше печатается, меньше потребляется. Поскольку ясно, что правильный ответ на ваш вопрос сводится к тому, чтобы быть вопросом вкуса, это также может быть веским аргументом.