A atualização do conjunto de C ++ STL é tediosa: não consigo alterar um elemento no lugar

Eu acho a operação de atualização no set tedioso, já que não há uma API como essa na cppreference . Então o que eu faço atualmente é sth assim:

//find element in set by iterator Element copy = *iterator; ... // update member value on copy, varies Set.erase(iterator); Set.insert(copy); 

Basicamente, o iterador retornado por Set é um const_iterator e você não pode alterar seu valor diretamente.

Existe uma maneira melhor de fazer isso? Ou talvez eu deva replace o set criando o meu (o que eu não sei exatamente como funciona ..)

   

set retorna const_iterators (o padrão diz que set::iterator é const , e que set::const_iterator e set::iterator pode na verdade ser o mesmo tipo – veja 23.2.4 / 6 no n3000 .pdf) porque é um contêiner ordenado. Se retornasse um iterator regular, você teria permissão para alterar o valor dos itens para fora do contêiner, possivelmente alterando a ordem.

Sua solução é a maneira idiomática de alterar itens em um set .

Existem duas maneiras de fazer isso, no caso fácil:

  • Você pode usar mutable na variável que não faz parte da chave
  • Você pode dividir sua class em um par de valores- Key (e usar um std::map )

Agora, a questão é para o caso complicado: o que acontece quando a atualização realmente modifica a parte key do object? Sua abordagem funciona, embora eu admita que é tedioso.

Atualização: Embora o seguinte seja verdade a partir de agora, o comportamento é considerado um defeito e será alterado na próxima versão do padrão. Que muito triste.


Existem vários pontos que tornam sua pergunta bastante confusa.

  1. Funções podem retornar valores, as classs não podem. std::set é uma class e, portanto, não pode retornar nada.
  2. Se você puder chamar s.erase(iter) , iter não será um const_iterator . erase requer um iterador não-constante.
  3. Todas as funções de membro de std::set que retornam um iterador retornam um iterador não-constante desde que o conjunto seja não-constante também.

Você tem permissão para alterar o valor de um elemento de um conjunto, desde que a atualização não altere a ordem dos elementos. O código a seguir compila e funciona bem.

 #include  int main() { std::set s; s.insert(10); s.insert(20); std::set::iterator iter = s.find(20); // OK *iter = 30; // error, the following changes the order of elements // *iter = 0; } 

Se a sua atualização alterar a ordem dos elementos, você terá que apagar e reinserir.

Você pode querer usar um std :: map. Use a parte do elemento que afeta a ordem da chave e coloque todos os elementos como o valor. Haverá alguma pequena duplicação de dados, mas você terá atualizações mais fáceis (e possivelmente mais rápidas).

Eu encontrei o mesmo problema em C ++ 11, onde de fato ::std::set::iterator é constante e, portanto, não permite alterar seu conteúdo, mesmo que saibamos que a transformação não afetará o < invariante . Você pode contornar isso ::std::set wrapping ::std::set em um tipo mutable_set ou escrevendo um wrapper para o conteúdo:

  template  struct MutableWrapper { mutable T data; MutableWrapper(T const& data) : data(data) {} MutableWrapper(T&& data) : data(data) {} MutableWrapper const& operator=(T const& data) { this->data = data; } operator T&() const { return data; } T* operator->() const { return &data; } friend bool operator< (MutableWrapper const& a, MutableWrapper const& b) { return a.data < b.data; } friend bool operator==(MutableWrapper const& a, MutableWrapper const& b) { return a.data == b.data; } friend bool operator!=(MutableWrapper const& a, MutableWrapper const& b) { return a.data != b.data; } }; 

Acho isso muito mais simples e funciona em 90% dos casos sem que o usuário sequer perceba que existe algo entre o conjunto e o tipo real.

Se o seu conjunto contém objects que você deseja alterar, certifique-se de armazenar seus pointers no conjunto.

Bem, isso não impede que você insira vários objects com o mesmo valor (bem, você não pode inserir o mesmo object várias vezes), mas é útil se você quiser um recipiente com inserção e remoção rápida.

Isso é mais rápido em alguns casos:

 std::pair::iterator, bool> result = Set.insert(value); if (!result.second) { Set.erase(result.first); Set.insert(value); } 

Se o valor geralmente não estiver no std :: set, isso pode ter um melhor desempenho.