1

I've created a unordered_set of my own type of struct. I have an iterator to this set and would like to increment a member (count) of the struct that the iterator points to. However, the compiler complains with the following message:

main.cpp:61:18: error: increment of member ‘SentimentWord::count’ in read-only object

How can I fix this?

Here's my code:

#include <fstream>
#include <iostream>
#include <cstdlib> 
#include <string>
#include <unordered_set>


using namespace std;


struct SentimentWord {
  string word;
  int count;
};


//hash function and equality definition - needed to used unordered_set with type   SentimentWord
struct SentimentWordHash {
  size_t operator () (const SentimentWord &sw) const;
};

bool operator == (SentimentWord const &lhs, SentimentWord const &rhs);



int main(int argc, char **argv){


  ifstream fin;
  int totalWords = 0;
  unordered_set<SentimentWord, SentimentWordHash> positiveWords;
  unordered_set<SentimentWord, SentimentWordHash> negativeWords;


  //needed for reading in sentiment words
  string line;
  SentimentWord temp;
  temp.count = 0;


  fin.open("positive_words.txt");
  while(!fin.eof()){
    getline(fin, line);
    temp.word = line;
    positiveWords.insert(temp);
  }
  fin.close();


  //needed for reading in input file
  unordered_set<SentimentWord, SentimentWordHash>::iterator iter;


  fin.open("041.html");
  while(!fin.eof()){
    totalWords++;
    fin >> line;
    temp.word = line;
    iter = positiveWords.find(temp);
    if(iter != positiveWords.end()){
      iter->count++;
    }
  }


  for(iter = positiveWords.begin(); iter != positiveWords.end(); ++iter){
    if(iter->count != 0){
      cout << iter->word << endl;
    }
  }


  return 0;

}


size_t SentimentWordHash::operator () (const SentimentWord &sw) const {
  return hash<string>()(sw.word);
}


bool operator == (SentimentWord const &lhs, SentimentWord const &rhs){
  if(lhs.word.compare(rhs.word) == 0){
    return true;
  }
  return false;
} 

Any help is greatly appreciated!

user2052561
  • 1,339
  • 2
  • 15
  • 18
  • [You fix this by not using a `std::set`](ftp://24.151.202.80/AiDisk_a1/Full/Completed/pdf/col1.pdf) – Mooing Duck May 09 '13 at 00:29
  • [Yet another code with the `eof` bug?](http://stackoverflow.com/questions/2251433/checking-for-eof-in-stringgetline) (Use getline in the while-condition instead...) – leemes May 09 '13 at 00:59

3 Answers3

5

Elements in an unordered_set are, by definition, immutable:

In an unordered_set, the value of an element is at the same time its key, that identifies it uniquely. Keys are immutable, therefore, the elements in an unordered_set cannot be modified once in the container - they can be inserted and removed, though.

I would vote that you use an unordered_map instead, using a string as the key and an int as the mapped value.

nebain
  • 113
  • 7
2

One solution (but a dirty hack) is to make your counter mutable, which means, that you permit to change it even on const objects.

struct SentimentWord {
  string word;
  mutable int count;
};

As I already said, this is a dirty hack, since it allows you to violate rules (you soften them). And rules have a reason. I'm not even sure if this works, since the definition of the unordered_set says that the values can't be modified once being inserted, and this also has a reason.

A nicer solution is to use a map which uses the word as a key and the counter as a value. Your code then doesn't have to use find but simply access the element using the subscript operator ("array access" operator) which directly returns a reference (not an iterator). On this reference, use the increment operator, like this:

std::unordered_map<std::string,int> positiveWords;
//...
positiveWords[word]++;

Then you don't need your struct at all, and of course also not your custom comparison operator overload.


Trick (just in case you need it): If you want to order a map by its value (if you need a statistical map with the most frequent words coming first), use a second (but ordered) map with reversed key and value. This will sort it by the original value, which is now the key. Iterate it in reverse order to start with the most frequent words (or construct it with std::greater<int> as the comparison operator, provided as the third template parameter).

leemes
  • 44,967
  • 21
  • 135
  • 183
0

std::unordered_set is unhappy because it's worried you will change the object in such a way it is the same as another object, which would violate the set. ISTM you really want a map from string to int (not a set at all), and the iterator will let you change the returned value, if not the key.

Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53