Search code examples
c++sortingsetshared-ptr

Make set of pointers to use comparison criteria of underlying injects, C++


I want std::set if shared_ptr's to compare the pointee's, not pointers.
I have this example:

std::shared_ptr<std::string> s(new std::string("abc"));
std::shared_ptr<std::string> p(new std::string("abc"));
std::set<std::shared_ptr<std::string>> S;
S.insert(s);
S.insert(p);
std::cout << S.size();

As you can see I am putting the same element in the set but this outputs 2.
How can I make set's insert to use the comparison criteria of underlying strings? And what if its not a string but more complex object?


Solution

  • The second template parameter of std::set is the type of the comparator to be used (default is std::less<Key>):

    #include <iostream>
    #include <memory>
    #include <set>
    #include <string>
    
    struct deref_less {
      bool operator()(const auto& a, const auto& b) const { return (*a) < (*b); }
      using is_transparent = void;
    };
    
    int main() {
      std::shared_ptr<std::string> s(new std::string("abc"));
      std::shared_ptr<std::string> p(new std::string("abc"));
      std::set<std::shared_ptr<std::string>, deref_less> S;
      S.insert(s);
      S.insert(p);
      std::cout << S.size();
    }
    

    Output:

    1
    

    auto parameters for convenience with C++20, before the comparator is a bit more verbose. using is_transparent = void; to enable eg the set::find overload that accepts a std::unique_ptr<std::string> (see godbolt example).