/* # set - unique elements: inserting twice does nothing - always ordered: $O(log)$ find / insert - immutable elements: it is not possible to modify an object, one must first remove it and resinsert. This is so because modification may mean reordering. */ #include "common.hpp" // Non key searches. class MemberKey { public: // Note that there is _no_ conversion constructor, // everything is done at the template level without // intermediate object creation. //MemberKey(int key) : key(key) {} MemberKey(int key, int notkey) : key(key), notkey(notkey) {} int key; int notkey; }; bool operator<(const MemberKey& c, int key) { return c.key < key; } bool operator<(int key, const MemberKey& c) { return key < c.key; } bool operator<(const MemberKey& c, const MemberKey& d) { return c.key < d.key; } //bool operator<(const std::unique_ptr& c, int key) { return *c < key; } //bool operator<(int key, const std::unique_ptr& c) { return key < *c; } // Works but not very cool as it hides the default unique_ptr::operator<, // which is meaningful for arrays like regular pointers are. //bool operator<(const std::unique_ptr& c, const std::unique_ptr& d) { //return c->key < d->key; //} struct PointerCmp { // Required for the heterogeneous searches to be found by compiler. // Value does not seem to matter, only presence, but std::true_type is a good choice semantically. typedef std::true_type is_transparent; typedef std::unique_ptr T; bool operator()(const T& c, int key) const { return *c < key; } bool operator()(int key, const T& c) const { return key < *c; } bool operator()(const T& lhs, const T& rhs) const { return lhs->key < rhs->key; } }; int main() { // C++11 initializer list { { std::set s{1, 2, 0, 1}; std::set s2{0, 1, 2}; assert(s == s2); } { std::set s = {"a", "c", "b", "a"}; std::set s1 = {"a","b", "c"}; assert(s == s1); } } // You can modify objects if you store pointers. { int i = 0; std::set s; s.insert(&i); std::set::iterator it = s.find(&i); *(*it) = 1; assert(i == 1); } /* # insert Return is a pair conatining: - if the item was not present, an iterator to the item inserted and true - if the item was present, an iterator to the existing item inserted and false */ { std::pair::iterator,bool> ret; std::set s; ret = s.insert(1); assert(ret.first == s.find(1)); assert(ret.second == true); ret = s.insert(2); assert(ret.first == s.find(2)); assert(ret.second == true); ret = s.insert(0); assert(ret.first == s.find(0)); assert(ret.second == true); // Item already present: // nothing is done and returns false on the pair. ret = s.insert(1); assert(ret.first == s.find(1)); assert(ret.second == false); std::set s1 = {0, 1, 2}; assert(s == s1); } /* # erase Remove element from set. Returns number of elements removed (either 0 or 1). */ { std::set s = {0, 1, 2}; assert(s.erase(1) == 1); std::set s2 = {0, 2}; assert(s == s2); assert(s.erase(1) == 0); } // ERROR no random access since it uses bidirection iterator. { //cout << s[0] << endl; } // size { std::set s; assert(s.size() == 0); s.insert(0); assert(s.size() == 1); } /* iterate Biderectional iterator. Always sorted by key: http://stackoverflow.com/questions/8833938/is-the-stdset-iteration-order-always-ascending-according-to-the-c-specificat/41424755#41424755 This basically forces a balanced tree implementation instead of hash set, and requires < > operators to be implemented on the value. Sorted by insertion order does not exist: http://stackoverflow.com/questions/7801109/does-the-c-standard-library-have-a-set-ordered-by-insertion-order */ { std::set s; s.insert(2); s.insert(1); auto it = s.begin(); assert(*it == 1); it++; assert(*it == 2); it++; assert(it == s.end()); } /* # find If found, returns an iterator pointing to the element. Else, returns `map::end()` find is `log n` time since the container is ordered. */ { std::set s = {0, 1, 2}; std::set::iterator it; it = s.find(1); assert(*it == 1); it = s.find(3); assert(it == s.end()); } /* count Count how many times an item is in the set. Can only return 1 or 0. Equivalent to doing a find. */ { std::set s = {1, 2, 0, 1}; assert(s.count(1) == 1); assert(s.count(3) == 0); } /* # Modify key. # Update Impossible without either: - remove and re-insert (possibly with hint), which is the right way - hacks like `mutable` Because `std::set::iterator` is actually `const` by the standard: - http://stackoverflow.com/questions/908949/what-happens-when-you-modify-an-element-of-an-stdset - http://stackoverflow.com/questions/2217878/c-stl-set-update-is-tedious-i-cant-change-an-element-in-place - http://stackoverflow.com/questions/7340434/how-to-update-an-existing-element-of-stdset - http://stackoverflow.com/questions/6068167/is-this-safe-mutability-and-sets # Hint If an iterator is given, it serves as a hint to where the new value is likely to be inserted. This is speciall useful if you want to update a value, but expect the new result to be in the same position. */ { class C { public: C(int i) : i(i) {} bool operator<(const C& other) const { return this->i < other.i; } bool operator==(const C& other) const { return this->i == other.i; } bool operator>(const C& other) const { return this->i > other.i; } mutable int i; }; std::set s; s.insert(C(1)); s.insert(C(2)); auto it = s.find(C(1)); //*it = C(3); // Almost certainly bad, but I can't find the C++ quote that says so. it->i = 3; } // Search by non-key comparable // - http://stackoverflow.com/questions/17375780/is-it-possible-to-use-elements-of-a-different-type-than-contained-in-a-stdset // - http://stackoverflow.com/questions/20317413/what-are-transparent-comparators { // Member is key, operators injected as comparisions with memeber make sense. // http://stackoverflow.com/questions/13827973/c-map-container-where-key-is-part-of-value/41624995#41624995 { std::set> s; s.insert(MemberKey(1, -1)); s.insert(MemberKey(2, -2)); s.insert(MemberKey(0, 0)); s.insert(MemberKey(3, -3)); assert(s.find(0)->notkey == 0); assert(s.find(1)->notkey == -1); assert(s.find(2)->notkey == -2); assert(s.find(3)->notkey == -3); assert(s.find(MemberKey(1, 1234))->notkey == -1); } // unique_ptr // - http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs // - http://stackoverflow.com/questions/17851088/using-a-stdunordered-set-of-stdunique-ptr { // Less good solution with a hidden operator<. { //std::set, std::less<>> s; //s.insert(std::make_unique(1, -1)); //s.insert(std::make_unique(2, -2)); //s.insert(std::make_unique(0, 0)); //s.insert(std::make_unique(3, -3)); //assert((*s.find(0))->notkey == 0); //assert((*s.find(1))->notkey == -1); //assert((*s.find(2))->notkey == -2); //assert((*s.find(3))->notkey == -3); } // Better solution with a custom comparator. { std::set, PointerCmp> s; s.insert(std::make_unique(1, -1)); s.insert(std::make_unique(2, -2)); s.insert(std::make_unique(0, 0)); s.insert(std::make_unique(3, -3)); assert((*s.find(0))->notkey == 0); assert((*s.find(1))->notkey == -1); assert((*s.find(2))->notkey == -2); assert((*s.find(3))->notkey == -3); } } } }