views:

527

answers:

2

I have an awkward hash table (specifically, an unordered_map) with int keys and vector< vector< int >> data. I periodically need to update elements in this two-dimensional vector of ints. There's no intrinsic reason I shouldn't be able to, right? A newer g++ compiler I've switched to complains of an assignment of read-only location on the line designated below.

typedef std::tr1::unordered_map< int, vector< vector< int > > > pimap;

vector< Strain * > liveStrains;
pimap phenotypeIs;
int NUM_DEMES = 3;

...
vector< Strain * >::const_iterator lsItr;
for ( lsItr = liveStrains.begin(); lsItr != liveStrains.end(); ++lsItr ) {
 int thisP = (*lsItr)->getPhenotype();
 pimap::iterator piItr = phenotypeIs.begin();
 piItr = phenotypeIs.find( thisP );
 if ( piItr != phenotypeIs.end() ) {
   for ( int d = 0; d < NUM_DEMES; d++ ) {
      ( piItr -> second )[ thisStep ].at( d ) = (*lsItr)->getI( d );  // error here
   }
 }
}

I'm new to C++, so nothing's too obvious. Thank you for any help.


Following Tim's suggestion

I've replaced the relevant parts of code above with the following:

  pimap::iterator piItr = phenotypeIs.find( thisP );
  if ( piItr != phenotypeIs.end() ) {
    for ( int d = 0; d < NUM_DEMES; d++ ) {
      vector< vector< int > > & thisVec2 = piItr->second;
      vector<int> & thisVec = thisVec2.at( thisStep );
      int & ii = thisVec.at( d );
      ii = (*lsItr)->getI( d );
      // ( piItr -> second )[ thisStep ].at( d ) = (*lsItr)->getI( d ); // error was here
    }

This code compiles without the error and appears to run fine. Like Tim, I still don't quite understand why the fix works. The error was previously appearing with gcc version 4.1.2 20080704 (Red Hat 4.1.2-44) but not with gcc version 4.0.1 (Apple Inc. build 5465). I will try to dissect the error more carefully when I'm not under a tight deadline!

+2  A: 

Are you sure there really are thisStep + 1 elements in every first-level vector and NUM_DEMES elements in every second-level vector?

You're not actually assigning to a map iterator, if I read correctly, so I suspect the error is in the vector access.

It may be helpful to break that last statement up into multiple statements so that only one thing is being done on each to narrow down where the problem is. e.g.,

Strain* strain = *lsItr;
vector<vector<int> >& vv = piItr->second;
vector<int>& v = vv[thisStep];
int& i = v.at(d);     // <-- My bet is that the error occurs here or the prev. line
i = strain->getI( d );

By the way, piItr = phenotypeIs.begin(); has no effect here, it could be simply:

pimap::iterator piItr = phenotypeIs.find( thisP );
Tim Sylvester
When I replace the line with the longer 'unpacked' version you propose, the error disappears and things appear to run fine. I will extensively test it in an hour or so. Thanks very much for help with the syntax... this implies there is a syntactical problem with what I had originally. (For the record, at one point, I had an old compiler that couldn't handle iterator declarations that didn't point somewhere, so I got in the habit of assigning them to .begin().)
Sarah
Odd, it should be 100% identical in terms of what methods and operators actually get called. It's just possible that you've found a bug in g++ or its STL. You could collapse it again one step at a time to try and find the problem, but it might be better to leave it expanded just for clarity.
Tim Sylvester
A: 
( piItr -> second )[ thisStep ].at( d )

at() returns an iterator into the inner vector, not access to the value. What you want is

 *(( piItr -> second )[ thisStep ].at( d ))
Chris
No, `vector::at()` returns a reference or const reference to the vector's element type. http://www.cplusplus.com/reference/stl/vector/at/
Tim Sylvester
Right, my mistake.
Chris