7

I have code like:

unordered_set<AttrValue> output;
...

auto requiredType = variables.at(arg.value);
auto end = remove_if(output.begin(), output.end(), 
    [&](AttrValue x) {
        return !matchingOutputType(requiredType, ast->getNodeType(ast->getNodeKeyAttribute(x)));
    }); // queryevaluator_getcandidatelist.cpp(179)
output.erase(end);

Error is on line 4 of the code. So I think its because of remove_if. But whats wrong? output is not defined constant?


Error   90  error C3892: '_Next' : you cannot assign to a variable that is const    c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm    1840
Error   109 error C3892: '_Next' : you cannot assign to a variable that is const    c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm    1840

Output window:

3>c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(1840): error C3892: '_Next' : you cannot assign to a variable that is const
3>          c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(1853) : see reference to function template instantiation '_FwdIt std::_Remove_if<std::_List_unchecked_const_iterator<_Mylist>,_Pr>(_FwdIt,_FwdIt,_Pr)' being compiled
3>          with
3>          [
3>              _FwdIt=std::_List_unchecked_const_iterator<std::_List_val<int,std::allocator<AttrValue>>>,
3>              _Mylist=std::_List_val<int,std::allocator<AttrValue>>,
3>              _Pr=`anonymous-namespace'::<lambda4>
3>          ]
3>          h:\dropbox\sch\cs3202\code\source\query\query evaluator\queryevaluator_getcandidatelist.cpp(179) : see reference to function template instantiation '_FwdIt std::remove_if<std::_List_const_iterator<_Mylist>,`anonymous-namespace'::<lambda4>>(_FwdIt,_FwdIt,_Pr)' being compiled
3>          with
3>          [
3>              _FwdIt=std::_List_const_iterator<std::_List_val<int,std::allocator<AttrValue>>>,
3>              _Mylist=std::_List_val<int,std::allocator<AttrValue>>,
3>              _Pr=`anonymous-namespace'::<lambda4>
3>          ]
Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • According to http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html `auto` can at times implicitly confer constness as well. Is this the case here? – Roman Saveljev Mar 30 '13 at 09:00
  • 2
    Possible duplicate of [Erase-remove idiom with std::set failing with constness-related error](http://stackoverflow.com/questions/3792600/erase-remove-idiom-with-stdset-failing-with-constness-related-error) – awesoon Mar 30 '13 at 09:12

2 Answers2

8

According to the standard § 23.2.4.6

For associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators.

So, you can't even do

std::unordered_set<int> s{1, 2, 3, 4};
*s.begin() = 42;

And, of course, you can't use std::remove_if(ForwardIt first, ForwardIt last, ...) function for removing elements from std::set and std::unordered_set:

The type of dereferenced ForwardIt must meet the requirements of MoveAssignable.

awesoon
  • 32,469
  • 11
  • 74
  • 99
  • So what should I do? I am using set here because essentially, I want to store unique values only. If I use a vector, I need to check if the value exists then insert. The straight-forward way is just convert the set to vector then work from there. Sounds inefficient? How can I work around this? – Jiew Meng Mar 30 '13 at 09:13
  • 2
    @JiewMeng, See the [link](http://stackoverflow.com/questions/3792600/erase-remove-idiom-with-stdset-failing-with-constness-related-error) in my comment to your question. – awesoon Mar 30 '13 at 09:14
0

The algorithm std::remove_if (not the member function unordered_set::remove_if, which is mentioned in the title but does not exist) doesn't actually remove anything; it reorders the sequence by overwriting elements that match the passed-in criterion with later elements in the sequence. So it requires a sequence of mutable objects. The objects in the sequence managed by an unordered_set are not mutable, since modifying them could disrupt the order that the container imposes.

To remove an element with a given key, use unordered_set::erase.

To remove an element or elements that match a more general criterion, you have to roll your own: iterate through the container looking for a match (std::find_if will do this), and remove elements as they're found using unordered_set::erase. Be careful: after you've erased an element the iterator that points to it is no longer valid, so your code has to save an iterator that points to the next element in the sequence. There are lots of examples of how to do this floating around.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165