mercredi 28 décembre 2016

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

A sorted ordering when iterating and printing the contents std::set implies that the _actual underlying values) are being compared.

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

The function ptr_set_with_custom_comparator works as expected. I am able to achieve what I want with my own shared_ptr_comparator.

However, the output of ptr_set_with_owner_less has left me puzzled. Is owner_less (or owner_before) utilizing the values of the pointers themselves?

Based on all of this, 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.

Aucun commentaire:

Enregistrer un commentaire