C ++ для цикла: оценка состояния - PullRequest
3 голосов
/ 28 октября 2011

У меня (глупый?) Вопрос C / C ++ о цикле:

for (size_t i = 0; i < std::distance(begin, end); ++i) {
  a.push_back(i);
}

begin и end - два итератора.У меня вопрос, std::distance(begin, end) рассчитывается для каждого элемента в цикле?Или лучше использовать эту версию:

size_t dist = std::distance(begin, end);
for (size_t i = 0; i < dist; ++i) {
  a.push_back(i);
}

Ответы [ 6 ]

6 голосов
/ 28 октября 2011

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

3 голосов
/ 28 октября 2011

Да. Вторая версия лучше.

Что касается первой версии, если тип контейнера a равен std::vector, и begin и end являются итераторами a, то операция push_back может вызвать изменение размера вектора, что, в свою очередь, приведет к недействительности итераторов begin и end, и их использование для вычисления расстояния в следующей итерации вызовет неопределенное поведение . В этом случае второй не только лучше, но и четко определен .

2 голосов
/ 28 октября 2011

Без оптимизации он действительно вычисляется каждый раз. На практике с оптимизацией это не должно иметь значения. Если крайне важно, чтобы он не рассчитывался каждый раз (например, потому что это ваша самая внутренняя задача), вы всегда можете быть осторожны и перейти ко второму.

2 голосов
/ 28 октября 2011

Скорее всего, это зависит от оптимизации компилятора. Если они выключены, std::distance будет выполняться в каждом цикле, и это точно. Причина - итераторы могут быть изменены внутри тела цикла.

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


РЕДАКТИРОВАТЬ Мой ответ предполагает, что begin и end в вашем коде НЕ begin и end вектора (или что бы то ни было) a. Если это так, то ответ на ваш вопрос зависит от того, что вы на самом деле пытаетесь сделать.

1 голос
/ 28 октября 2011

Способность компилятора выполнить оптимизацию зависит (как минимум) от задействованных типов.Например, если begin и end являются итераторами списка, а a является списком, а end является итератором в конце a, то это бесконечный цикл (пока не завершитсяпамять происходит).Компилятор не может выполнить «оптимизацию», если он меняет смысл программы.Предположительно, это не меняет смысла программы, иначе вы бы не задали вопрос, но все же компилятор должен как-то исключить такую ​​возможность, например, отслеживая значение end, где бы оно ни было установлено.В некоторых случаях это может быть очевидно для вас, но компилятор не может доказать.

И наоборот, если begin и end являются векторными итераторами, то на практике они являются либо указателями, либотонкая обертка вокруг одного.Тогда компилятор вполне может увидеть, что их значения никогда не изменятся в цикле, и, следовательно, их расстояние никогда не изменится, и выполнить оптимизацию.Даже если оптимизация не выполняется, distance дешево для векторных итераторов.Таким образом, оптимизация может быть не столь значимой в любом случае в этом случае.

И наоборот, проверка реализации векторного итератора может теоретически включать в себя код, чтобы увидеть, был ли вектор перераспределен с момента его выбора (и, следовательно, итератор больше не действителен).Этот факт может изменить в цикле, поэтому, если std::distance косвенно вызывает эту проверку, то в этой реализации ее невозможно поднять.Не то чтобы вы обычно комбинировали проверочный итератор с оптимизацией, но это возможно.

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

А для вашего второго фрагмента некоторые люди предпочитают этот стиль:

for (size_t i = 0, dist = std::distance(begin, end); i < dist; ++i) {
  a.push_back(i);
}

Он основан на одинаковых типах в сравнении, но избегает помещения dist в окружающую область, где это не обязательно.

0 голосов
/ 28 октября 2011

Оба будут иметь такую ​​же производительность, как любой приличный компилятор преобразует код во вторую версию. Если оптимизация компилятора отключена, используйте вторую версию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...