Search code examples
c++vectorreferencereference-wrapper

Where's my bad_alloc coming from?


I have this code. scores is a field in HiscoreTable of type std::vector<reference_wrapper<Record>>

void HiscoreTable::replaceRecord(Record rec)
{
  bool found = false;
  for (reference_wrapper<Record> r : scores) {
    if (r.get().name == rec.name) {
      r = rec;
      found = true;
      break;
    }
  }
  if (!found) {
    scores.push_back(rec);
  }
}

And if I try to do for(reference_wrapper<Record> r : scores) cout << r.get() << endl;, a bad_alloc is thrown. Is it because r is not a Record& but a reference_wrapper<Record> and therefore I cannot assign a Record to it? I thought reference_wrapper<Record>::operator= was overloaded to allow such a thing?

EDIT: Turns out it was a problem in my destructor, where the printing loop was located. At some point, I passed a HiscoreTable in to a helper function by value, and when it got destructed at the end of the helper function, it invalidated the callsite's HiscoreTable. I don't know why printing would break a reference, but I guess that's what happened.


Solution

  • For starters, you are creating a reference to a local object.

    void HiscoreTable::replaceRecord(Record rec) { // 'rec' passed by value.
        bool found = false;
        for (reference_wrapper<Record> r : scores) {
            if (r.get().name == rec.name) {
            r = rec; // Reference to local object 'rec'.
    
            /* ... */
    
    } // 'rec' is destroyed and any reference to it will become dangling.
    

    The assignment operator of std::reference_wrapper will replace the internal "reference" with the one passed as argument, e.g.

    r = rec; // Reference wrapper 'r' will contain pointer to 'rec'.
    

    Instead pass rec by reference. Also for the replacement to stick, you will need to change the type of r in the for-loop to be a reference. Otherwise you are assigning to a local copy.

    for (reference_wrapper<Record>& r : scores) { // Type changed to reference.
        /* ... */
    }