1

I have the following immutable container class (public access to values is just for reasons of simplicity):

struct Container
{
    std::unordered_set<int> values;

    //Default constructor
    Container() = default;

    //Copy constructor
    Container(const Container& other)
        : values(other.values)
    {   }

    //Move constructor
    Container(const Container&& other)
        : values(std::move(other.values))
    {   }

    Container RemoveValue(int value) const
    {
        //Create a copy of this object
        Container copy(*this);
        //Remove one element from the copy
        copy.values.erase(value);
        return copy;
    }
};

This container contains a set of values. The method RemoveValue() returns a copy of the current object where a specific value has been removed. An appropriate move constructor is defined for this struct.

I use this container as follows:

int main() 
{
    std::vector<Container> containers;

    {
        //Initialization
        Container initialContainer;
        initialContainer.values.insert(1);
        initialContainer.values.insert(2);

        containers.push_back(std::move(initialContainer));
    }

    const Container* currentContainer = &containers.front();
    for (int value : currentContainer->values)
    {
        Container newContainer = currentContainer->RemoveValue(value);

        //Do some checks, then...
        containers.push_back(std::move(newContainer));
    }

    std::cout << containers.size() << std::endl;

    return 0;
}

I initialize a vector of containers with a single container (with values 1 and 2). Then, I acquire a pointer to this initial element and iterate every value. For each value, I call RemoveValue() and insert the resulting container into the vector.

In gcc, this seems to work just fine. However, I get runtime errors in Visual Studio 2015.

In Debug Mode, the error is: "list iterator not incrementable". This error occurs after the first iteration at for (int value : currentContainer->values) (when the iterator is to be incremented).

In Release Mode, the error is: "Access violation reading at position 0x38". This error occurs at copy.values.erase(value) in RemoveValue. But only in the second iteration. Surprisingly, values does not contain elements at this point, anymore (size() returns 0).

I don't understand either of these errors. How can I resolve them?

A C++ Shell example also runs without errors. However, it outputs 2 as the final number of containers, whereas I expected three (the initial one, one with 1 removed, one with 2 removed).

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70

1 Answers1

2

currentContainer is a pointer to an element of the vector containers. The loop body modifies containers by calling push_back. That can invalidate pointers into the vector, and if it does, currentContainer can end up pointing to garbage.

In general, don't use pointers to objects that are held in an std::vector. Instead, use containers.front() or containers[0] to get at the first element.

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