Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an STL comparator for std::set (or std::map) with shared_ptr keys that provides value-based lookups? What exactly does std::owner_less do?

I have a std::map with shared_ptr<T> keys, and I need it to use the actual value (of type T, i.e. *key) for lookups, not the value of the shared pointer itself.

I know I can write my own custom comparator (as I've done below), but I was wondering if the STL supplies a comparator specifically for this purpose.

To demonstrate what I'm talking about, I created this simple example that uses a std::set of strings (I've also put it on GitHub as a gist):

#include <set>
#include <string>
#include <memory>
#include <iostream>
#include <functional>

template< typename T >
struct shared_ptr_comparator {
  bool operator()(const std::shared_ptr<T> &a, const std::shared_ptr<T> &b) const {
    return std::less<T>()(*a, *b);
  }
};

void ptr_set_with_custom_comparator() {
    std::set< std::shared_ptr<std::string>, shared_ptr_comparator<std::string> > ptr_set;

    ptr_set.insert(std::make_shared<std::string>("world"));
    ptr_set.insert(std::make_shared<std::string>("hello"));
    ptr_set.insert(std::make_shared<std::string>("abc"));

    for(auto const& entry : ptr_set) {
        std::cout << *entry << std::endl;
    }
}

void ptr_set_with_owner_less() {
    std::set< std::shared_ptr<std::string>, std::owner_less<std::shared_ptr<std::string>> > ptr_set;

    ptr_set.insert(std::make_shared<std::string>("world"));
    ptr_set.insert(std::make_shared<std::string>("hello"));
    ptr_set.insert(std::make_shared<std::string>("abc"));

    for(auto const& entry : ptr_set) {
        std::cout << *entry << std::endl;
    }
}

void raw_set() {
    std::set<std::string> raw_set;

    raw_set.insert("world");
    raw_set.insert("hello");
    raw_set.insert("abc");

    for(auto const& entry : raw_set) {
        std::cout << entry << std::endl;
    }
}

int main() {
    std::cout << "A basic set of strings:" << std::endl;
    raw_set();
    std::cout << std::endl;

    std::cout << "A set of shared_ptr<string>s with owner_less as the comparator:" << std::endl;
    ptr_set_with_owner_less();
    std::cout << std::endl;

    std::cout << "A set of shared_ptr<string>s with the comparator shared_ptr_comparator:" << std::endl;
    ptr_set_with_custom_comparator();

    return 0;
}

The code above can be complied with clang++ -Wall -std=c++11. Here's the output:

A basic set of strings:
abc
hello
world

A set of shared_ptr<string>s with owner_less as the comparator:
world
hello
abc

A set of shared_ptr<string>s with the comparator shared_ptr_comparator:
abc
hello
world

Here, a sorted ordering when iterating and printing the contents std::set implies that the _actual underlying values) are being compared. A quick overview of the example above:

  • The function raw_set just uses set<string> (doesn't use shared_ptr), and is present for reference.

  • I am able to achieve what I want with my hand-written shared_ptr_comparator. The function ptr_set_with_custom_comparator which utilizes it, works as expected.

  • The function ptr_set_with_owner_less did not work as expected. Does owner_less (or owner_before) rely on the addresses/values of the pointers themselves?

I have two questions:

  • Does anything equivalent to shared_ptr_comparator (defined in the program above), exist in the STL? I ask because the comparator I wrote seems like a really common use case, and I would be very surprised if the STL didn't have anything equivalent to it.

  • What exactly does owner_less and owner_before (which it calls) do? Do they simply check for equivalence of the underlying pointers? I'm not sure if I'm using it right.

Thanks in advance for any answers to this question.

like image 387
Arjun Menon Avatar asked Dec 29 '16 07:12

Arjun Menon


People also ask

Is shared_ptr copyable?

The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr .

Where is std :: shared_ptr defined?

If your C++ implementation supports C++11 (or at least the C++11 shared_ptr ), then std::shared_ptr will be defined in <memory> . If your C++ implementation supports the C++ TR1 library extensions, then std::tr1::shared_ptr will likely be in <memory> (Microsoft Visual C++) or <tr1/memory> (g++'s libstdc++).

What happens when you move a shared_ptr?

By moving the shared_ptr instead of copying it, we "steal" the atomic reference count and we nullify the other shared_ptr . "stealing" the reference count is not atomic, and it is hundred times faster than copying the shared_ptr (and causing atomic reference increment or decrement).

What is C++ shared_ptr?

The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.


1 Answers

Does anything equivalent to shared_ptr_comparator (defined in the program above), exist in the STL? I ask because the comparator I wrote seems like a really common use case, and I would be very surprised if the STL didn't have anything equivalent to it.

I'm suprised too, but no, STL doesn't have built-in comparator for pointers that works this way. However there's better way to implement it which allows passing any pointer.

template<typename T, typename comp_t>
bool ptr_compare(T lhs, T rhs, comp_t comp) {
    return comp(*lhs, *rhs);
}

You can call it that way:

ptr_compare(a_ptr, b_ptr, std::less<int>())

And if you want a version compatible with STL containers:

template<typename T>
bool ptr_less(T lhs, T rhs) {
    return std::less<decltype(*lhs)>()(*lhs, *rhs);
}

What exactly does owner_less and owner_before (which it calls) do? Do they simply check for equivalence of the underlying pointers? I'm not sure if I'm using it right.

std::owner_less doesn't compare by value, but by owner, so it isn't relevant to your problem.

std::shared_ptr<T>::owner_before is called by std::owner_less to find out the ordering.

like image 89
Kamil Koczurek Avatar answered Oct 15 '22 11:10

Kamil Koczurek