Search code examples
c++stlstdset

spurious "containment" in std::set of pointers


A set of pointers is reporting that it "contains" a brand-new object, even though this object was just created, and has a memory address which differs from those already in the set.

#include <iostream>
#include <set>
#include <vector>

using namespace std;

class foo {
  public:
    int x;
    foo(int x) : x(x) {}
};

int main() {
    vector<foo> my_vector;
    set<foo*, bool (*)(foo*, foo*)> my_set([](foo* left, foo* right) { return left->x < right->x; });

    my_vector.reserve(10);
    for (int i = 0; i < 10; i++) {
        my_vector.emplace_back(i);
        my_set.emplace(&my_vector[i]);
    }
    foo temp(4);
    if (my_set.count(&temp))
        cout << "But why?" << endl;
    return 0;
}

This behavior DOES NOT occur if temp is initialized using an integer outside the range [0, 10). It's as if the integer x is being used to determine equality, as opposed to just priority / precedence.

The behavior I want is that distinct objects (i.e., with distinct memory locations) always be treated as distinct objects in the set, and that the objects' values x be used to order the elements. How do I achieve this behavior? Thanks.


Solution

  • Thanks all for the comments. Here is a solution that achieves the desired behavior:

    int main() {
        set<foo*, bool (*)(foo*, foo*)> my_set([](foo* left, foo* right) {
            if (left->x == right->x)
                return std::less<foo*>{}(left, right);
            return left->x < right->x;
        });
        // ....
        return 0;
    }
    

    EDITED to reflect @Igor Tandetnik's helpful comment!