1

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?

Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • 1
    What about writing a [custom comparator](https://stackoverflow.com/questions/2620862/using-custom-stdset-comparator)? – Evg Mar 10 '21 at 11:38

1 Answers1

5

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).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • "Missing" `using is_transparent = void;` :-) – Jarod42 Mar 10 '21 at 11:52
  • 2
    `compare_shared` is not the best name (`compare` tends to return negative, null, positive), how about `deref_less`? – Jarod42 Mar 10 '21 at 11:54
  • @Jarod42 didnt like the name either, thanks for the suggestion. I don't quite get the purpose of `is_transparent` yet. Found the templated overload for `std::set::find` that takes some param of type `K` that can be compared to `Key`s, but then do I not have to provide a `std::string < std::shared_ptr` comparison? – 463035818_is_not_an_ai Mar 10 '21 at 11:58
  • Here, you might compare `shared_ptr` with `unique_ptr` with raw pointer. `std::less` is *transparent* but you cannot compare anything with anything. So you don't have to provide `T* < T`. – Jarod42 Mar 10 '21 at 12:50
  • @Jarod42 got it. Thanks. – 463035818_is_not_an_ai Mar 10 '21 at 20:18