In C++ by default both std::set and std::multiset have std::less<T> as their comparator. Can anyone please explain how does std::multiset allow duplicates and std::set does not?
2 Answers
Both start with the equivalent an upper_bound on the existing contents to find the correct insertion point for the new item.
std::set then checks whether that found an existing item with a key equal to the new key, and if so returns, signaling failure.
std::multiset just inserts the new item at that point (and if it didn't return in the step above, std::set does the same).
 
    
    - 476,176
- 80
- 629
- 1,111
- 
                    3I think std::set is checking for equivalence, not equality, isn't it? In particular, one can use a type that only defines `operator<` (strict weak ordering), but not `operator==`. – vsoftco May 20 '15 at 02:54
- 
                    Does this mean the return value of `upper_bound` can be effectively used as a hint to `insert`? – Lingxi May 20 '15 at 03:07
- 
                    @Lingxi probably, but it is not going to improve on the total number of comparisons required to insert, since you pay for the `upper_bound`. – vsoftco May 20 '15 at 03:12
To follow up on Jerry's answer, note that std::set and std::multiset assume that the elements are comparable via a strict weak ordering by operator<. In particular, the elements do not have to be comparable under operator==.  std::set only allows non-equivalent elements, whereas std::multiset allows in addition equivalent elements. This is slightly different from equality/non-equality. Two elements A and B are equivalent whenever !(A < B) && !(B < A), and it is this latter condition that is checked by std::set::insert, and if true, the element is not inserted.
Example Live on Ideone
#include <iostream>
#include <set>
struct Foo
{
    int _x, _y, _z;
    Foo(int x, int y, int z): _x(x), _y(y), _z(z) {}
    bool operator<(const Foo& rhs) const // weak majorization
    {
        return (_x < rhs._x) && (_x + _y < rhs._x + rhs._y) &&
               (_x + _y + _z < rhs._x + rhs._y + rhs._z) ; 
    }
};
int main()
{
    std::set<Foo> setFoo;
    // try to insert 2 equivalent elements
    setFoo.insert(Foo{1, 2, 3}); 
    if(setFoo.insert(Foo{1, 2, 0}).second == false) // failed to insert
        std::cout << "Equivalent element already in the set!" << std::endl;
}
 
     
     
    