0

I want to search by raw pointer in a set of std::unique_ptr, and instead of writing my own comparator class I decided to use the transparent property of std::less<>.

Here's an example code based on the cppreference example.

This doesn't compile:

#include <memory>
#include <set>

using FatKey = std::unique_ptr<int>;
using LightKey = int;

bool operator<(const FatKey& fk, LightKey* lk) { return fk.get() < lk; }
bool operator<(LightKey* lk, const FatKey& fk) { return lk < fk.get(); }
bool operator<(const FatKey& fk1, const FatKey& fk2) { return fk1.get() < fk2.get(); }

int main()
{  
    std::set<FatKey, std::less<>> example2;
 
    LightKey lk = 2;
    auto search2 = example2.find(&lk);
}

While this works fine:

#include <memory>
#include <set>

template<typename T>
struct UPtrWrapper { std::unique_ptr<T> ptr; };

using FatKey = UPtrWrapper<int>;
using LightKey = int;

bool operator<(const FatKey& fk, LightKey* lk) { return fk.ptr.get() < lk; }
bool operator<(LightKey* lk, const FatKey& fk) { return lk < fk.ptr.get(); }
bool operator<(const FatKey& fk1, const FatKey& fk2) { return fk1.ptr.get() < fk2.ptr.get(); }

int main()
{  
    std::set<FatKey, std::less<>> example2;
 
    LightKey lk = 2;
    auto search2 = example2.find(&lk);
}

The FatKey is passed by const ref in both cases, they are both template classes, neither of them is copy constructible, but still only one of them works.

What am I missing here?

2 Answers2

2

Neither std::unique_ptr<int> nor int involve user-defined types; in terms of ADL their associated namespaces are namespace std only.

When you use a wrapper type you have an associated namespace of the root namespace (the namespace where you declared the wrapper), which means that ADL can find the free operator<s defined there.

Indeed, this is a Good Thing since it prevents someone else writing their own operator<(int*, std::unique_ptr<int> const&) which might have a different behavior to yours.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • "*which is, to be clear, not actually OK since you shouldn't inherit from standard library types*" The OP didn't use inheritance. Unless there was an older version of the question that did. – Nicol Bolas Oct 30 '20 at 17:01
  • @NicolBolas oops, yeah. Fixing. – ecatmur Oct 30 '20 at 17:02
-1

ecatmur already answered that the first example didn't work due to ADL.

I just want to show how you can create your std::set without needing all these extra functions:

auto PtrCmp = [](std::unique_ptr<int> const & lhs,
    std::unique_ptr<int> const & rhs) {
  return lhs.get() < rhs.get();
};

auto orderedPtrSet = std::set<std::unique_ptr<int>, decltype(PtrCmp)>(PtrCmp);

Note: I realize this is not a real answer to the question, but it's a little too long to put in a comment. And it seems to me it might be interesting for OP.

AVH
  • 11,349
  • 4
  • 34
  • 43
  • I downvoted this for two reasons: default-constructible stateless lambdas have only been introduced in C++20, and my question was about transparent comparators for comparing `unique_ptr`s by identity, not by value – Balázs Kovacsics Oct 31 '20 at 15:09
  • @BalázsKovacsics Ah, I didn't notice I had my compiler set to C++20. I also missed you were comparing the pointers themselves. The example now compares correctly and works for C++14. – AVH Oct 31 '20 at 17:03