Сохраняет ли unordered_set::erase(pos) порядок элементов?

Я читал в стандарте С++ 14, что порядок элементов сохраняется при использовании erase(iterator pos) из unordered_set.

Я попробовал следующий код с g++-6.2.0 и clang-3.9 (хотя в Linux это stdlib этого gcc). Я думаю, что оба должны быть в состоянии справиться с этим по спецификации С++ 14:

#include <unordered_set>
#include <iostream>
using std::unordered_set; using std::cout;

// output
template<typename Elem, typename Comp>
std::ostream& operator<<(std::ostream&os, const unordered_set<Elem,Comp>&data) {
    for(auto &e : data) { os << e << ' '; } return os << '\n'; }

int main() {
  unordered_set<int> nums{ 1,2,3,4,5,6,7,8,9,10 };
  cout << nums; // MSVC: 9 1 2 3 4 5 6 7 8 10
  for(auto it = nums.begin(); it!=nums.end(); ++it) {
    if(*it % 2 == 0) {
      nums.erase(it);
    }
  }
  cout << nums; // MSCV: 9 1 3 5 7
}

Да, порядок элементов произвольный. Здесь у MSVC++ 19.00 было 9 1 2 3 4 5 6 7 8 10. И после стирания всех четных элементов оставшиеся элементы остаются в том же порядке 9 1 3 5 7.

Однако с g++ и clang++ я получил совершенно плохой результат

10 9 8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1

что, по-видимому, указывает на то, что порядок элементов не сохранялся между вызовами, а просто... я не знаю.

Что происходит?


person towi    schedule 13.11.2016    source источник
comment
Стандарт содержит более 1000 страниц текста. Сказать, что вы прочитали это в стандарте, бесполезно. Где в стандарте вы это прочитали?   -  person    schedule 13.11.2016


Ответы (1)


Я предполагаю, что этот цикл неверен:

for(auto it = nums.begin(); it!=nums.end(); ++it) {
    if(*it % 2 == 0) {
        nums.erase(it);
    }
}

Если выполняется erase, то это становится недействительным, и вы не можете увеличить его. Предположительно, это вызывает вышеупомянутое поведение.

Вы должны использовать что-то вроде этого:

for(auto it = nums.begin(); it!=nums.end();) {
    if(*it % 2 == 0) {
        nums.erase(it++);
    } else {
        ++it;
    }
}
person Edgar Rokjān    schedule 13.11.2016
comment
Или вы можете просто использовать возвращаемое значение «стирать». - person Jesper Juhl; 13.11.2016
comment
@JesperJuhl только с С++ 11. - person Edgar Rokjān; 13.11.2016
comment
Которому сейчас 5 лет, и он должен быть минимальным стандартом (нет, зачеркните это, С++ 14 должен быть) - но да, конечно. - person Jesper Juhl; 13.11.2016
comment
В некоторых местах (например, на моей текущей работе) C++03 используется по умолчанию. Поэтому я подсознательно использую его в качестве стандарта по умолчанию в некоторых своих ответах :) Очевидно, что этот вопрос относится к стране C++1z, поэтому описанная вами форма стирания может быть предпочтительнее. - person Edgar Rokjān; 13.11.2016