Erase-remove idiom с std:: set с ошибкой, связанной с константой

Может кто-нибудь помочь мне здесь?

Компиляция этого кода:

void test()
{
  std::set<int> test;
  test.insert(42);
  test.erase(std::remove(test.begin(), test.end(), 30), test.end());  // <- Line 33
}

Создает следующую ошибку при компиляции:

$ make
g++ -c -Wall -pedantic-errors -Wextra -Wunused -Werror a_star.cpp
/usr/lib/gcc/i686-pc-cygwin/4.3.4/include/c++/bits/stl_algo.h: In function `_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = std::_Rb_tree_const_iterator<int>, _Tp = int]':
a_star.cpp:33:   instantiated from here
/usr/lib/gcc/i686-pc-cygwin/4.3.4/include/c++/bits/stl_algo.h:779: error: assignment of read-only location `__result.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int]()'
make: *** [a_star.o] Error 1

Ответы

Ответ 1

В std::set элементы не изменяются. Таким образом, std::set::iterator также немодифицируется. Из этот учебник, раздел 27.3.2.1:

В простых ассоциативных контейнерах, где элементы являются ключами, элементы абсолютно неизменяемы; вложенные типы итератора и const_iterator, следовательно, одинаковы.

Следовательно, идиома erase-remove не может применяться как есть. Вы должны написать цикл for и использовать внутри него функцию-член std::set::erase. Смотрите question, и этот принятый ответ и еще один ответ для получения точных данных, но, короче, цикл выглядит следующим образом

typename std::set::iterator set_iter; 

for( set_iter it = s.begin(); it != s.end(); /* blank */ ) {
    if( some_condition() ) {
        s.erase( it++ );       // Note the subtlety here
    }
    else {
        ++it;
    }
}

Ответ 2

Идиома Erase-remove не может использоваться с ассоциативными контейнерами. Ассоциативные контейнеры не допускают модификаций всего элемента контейнера через итератор, что сразу же означает, что операции с мутирующей последовательностью (например, std::remove) не могут быть применены к ним.

Ответ 3

Как уже говорилось, ваш код не работает, потому что вы пытаетесь изменить последовательность внутри ассоциативного контейнера, но вы не можете этого сделать, потому что эта последовательность неизменна. Обоснование: set содержит упорядоченную последовательность, обычно в двоичном дереве. Если вам разрешили изменить его, вы можете повредить контейнер, и программа выйдет из строя. Кстати, это может произойти в некоторых ситуациях.

Вы можете изменить свой код на это:

test.erase(30);

Или используйте код ArunSaha (+1) для более сложных критериев.

Ответ 4

Если я хорошо помню, std:: remove никогда не будет использоваться с элементом std:: set.

Поскольку набор не является чистым массивом, вы должны использовать стирание.