Каково объяснение поведения it++
и ++it
в контексте вызова функции --and более конкретно, для функции iter_swap
? Это смущает меня, что вызов 2 и вызов 4 в приведенном ниже коде появляются для замены элементов, тогда как вызов 3 и вызов 5 не могут заменить элементы. Однако я ожидал противоположного результата, но предполагал, что одна из записей параметров функции будет выполняться перед другой. Глядя на результат, похоже, это не так.
Итак, из любопытства, определяется ли это поведение? Как я могу понять, в каком порядке выполняются действия? Благодарю!
#include <iostream>
#include <vector>
#include <algorithm>
int main(void) {
vector<int> a;
a.push_back(1);
a.push_back(2);
vector<int>::iterator it_a, it_b;
it_a = a.begin();
it_b = it_a + 1;
cout << *it_a << " " << *it_b << endl;
// call 1
iter_swap(it_a, it_b);
cout << *it_a << " " << *it_b << endl;
// call 2
iter_swap(it_a, it_a++);
cout << *--it_a << " " << *it_b << endl;
// call 3
iter_swap(it_a, ++it_a);
cout << *--it_a << " " << *it_b << endl;
// call 4
iter_swap(it_a++, it_a);
cout << *--it_a << " " << *it_b << endl;
// call 5
iter_swap(++it_a, it_a);
cout << *--it_a << " " << *it_b << endl;
return 0;
}
выходы:
1 2
2 1
1 2
1 2
2 1
2 1
Большинство из этих примеров не имеют определенного поведения (единственным вызовом является вызов 1). Оценка аргументов функции не имеет никакого значения, что означает, что их порядок оценки неуточнен, поэтому вопрос о том, был ли побочный эффект operator++
другим аргументом к моменту operator++
функции, не определен. С другим компилятором вы могли бы получить другой результат, и оба они были бы совершенно стандартными.
Добавление: Некоторое объяснение семантики в порядке, я считаю.
До С++ 11 я бы говорил о точках последовательности здесь, но стандартный язык изменился, чтобы быть яснее, не меняя очень многого. Поэтому вместо этого я расскажу о последовательности операций.
Как правило, операции на С++ 11 частично упорядочены по времени. Иными словами, две операции (пусть их называют O и P) могут быть секвенированы, так что O секвенируется до P, что O секвенируется после P или что O и P неопределенно секвенированы или что O и P не подвержены влиянию.
Первые два являются простыми: если O секвенирован до P, все его эффекты должны были произойти к моменту времени P оценивается, если O секвенирован после P, все эффекты P появлялись, когда O приближается.
Что касается двух других: если O и P неопределенно секвенированы, то либо все эффекты O отображаются до P, либо все эффекты P появляются до O. Если они не подвержены влиянию, это отличная свобода для всех: некоторые эффекты O могут появиться перед некоторыми эффектами P, и в то же время некоторые эффекты P могут появиться до появления всех эффектов O - фактически, оценка O и P могут перекрываться.
Это следует рассматривать в контексте оптимизации: оптимизация компиляторов позволяет переупорядочить код, чтобы он работал быстрее. Частичные спецификации с точки зрения последовательности позволяют им делать гораздо больше этого - возможно, O имеет промежуточный результат, полезный для оценки P, такого рода вещи. Если вы считаете, например, большое арифметическое выражение, нетрудно понять, насколько слабая последовательность может помочь в векторизации и при наличии ограниченного числа регистров.
Итак, где ваши примеры падают?
В разделе 1.4 (15) С++ 11 упоминается, что "вычисления значений и побочные эффекты, связанные с разными выражениями аргументов, не имеют никакого значения". Таким образом, вы находитесь в глубоком конце здесь, плавая с драконами.