Search code examples
c++foreachstlunordered-set

c++ unordered set range-erase bug



std::unordered_set<Vector2i> basicWalls = FindWalls(floorPositions, cardinal2D);
std::unordered_set<Vector2i> cornerWalls = FindWalls(floorPositions, Diagonal2D);

    printf("basic walls size: %d, corner walls size: %d\n", basicWalls.size(), cornerWalls.size());

cornerWalls.erase(basicWalls.begin(),basicWalls.end());//to delete duplicates

    printf("basic walls size: %d, corner walls size: %d\n", basicWalls.size(), cornerWalls.size());

Wall_World_positions.reserve(basicWalls.size() + cornerWalls.size());
Wall_CollisionBox.reserve(basicWalls.size() + cornerWalls.size());

LoadAppropriateWallTiles(basicWalls, floorPositions, cardinal2D);

unordered set 'range-erase' invalidates basicwalls's iterator, not that of 'cornerWalls' whose elements are being erased.

cplusplus.com says 'Only the iterators and references to the elements removed are invalidated.'

Am I missing something, shouldn't it be the other way around? I am not trying to delete anything inside basicWalls.

when I comment out cornerWalls.erase, 'LoadAppropriateWallTiles' works as intented

error png


Solution

  • Per cppreference:

    Removes the elements in the range [first, last), which must be a valid range in *this.

    [basicWalls.begin(),basicWalls.end()) is not a valid range in cornerWalls, since the iterators come from different container.


    If you want to remove from cornerWalls all elements that exist in basicWalls, you need std::erase_if() from C++20:

    std::erase_if(cornerWalls, [&basicWalls](const Vector2i& v){return basicWalls.contains(v); });
    

    or before C++20 do it manually with erase:

    for(const Vector2i& v: basicWalls)
    {
        cornerWalls.erase(v);
    }
    

    std::set_difference requires sorted ranges, so it cannot be used with std::unordered_set.


    Also, be aware that %d is wrong specifier for std::size_t in printf. Lying to the function about type of data can end very badly. Use C++ output streams instead:

    std::cout << "basic walls size: " << basicWalls.size() << ", corner walls size: " << cornerWalls.size() << "\n";
    

    Or if you really need printf for some reason, use correct specifiers:

    printf("basic walls size: %zu, corner walls size: %zu\n", basicWalls.size(), cornerWalls.size());