Существует ли компаратор STL для std:: set (или std::map) с общими ключами ptr, который обеспечивает поиск значений? Что именно делает std:: owner less?


У меня есть std::map с ключами shared_ptr<T>, и мне нужно использовать фактическое значение (типа T, т. е. *key) для поиска, а не значение самого общего указателя.

Я знаю, что могу написать свой собственный компаратор (как я сделал ниже), но мне было интересно, поставляет ли STL компаратор специально для этой цели.

Чтобы продемонстрировать то, о чем я говорю, я создал этот простой пример, который использует std::set строк (я также поместил его на GitHub в качестве суть):

#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;
}

Вышеуказанный код может быть соблюден clang++ -Wall -std=c++11. Вот результат:

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

Здесь сортированный порядок при итерации и печати содержимого std::set подразумевает, что сравниваются _актуальные базовые значения). Краткий обзор приведенного выше примера:

  • Функция raw_set просто использует set<string> (не использует shared_ptr) и присутствует для ссылки.

  • Я могу добиться того, чего хочу, с помощью моего рукописного shared_ptr_comparator. То функция ptr_set_with_custom_comparator, которая его использует, работает как и ожидалось.

  • Функция ptr_set_with_owner_less работала не так, как ожидалось. Зависит ли owner_less (или owner_before) от адресов / значений самих указателей?

У меня есть два вопроса:

  • Существует ли в STL что-либо эквивалентное shared_ptr_comparator (определенному в программе выше)? Я спрашиваю, потому что компаратор, который я написал, кажется действительно распространенным случаем использования, и я был бы очень удивлен, если бы в STL ничего не было равносильно этому.

  • Что именно делает owner_less и owner_before (который он называет)? Они просто проверяют эквивалентность нижележащих указателей? Я не уверен, правильно ли я его использую.

Заранее Благодарю за любые ответы на этот вопрос.
1 3

1 ответ:

Существует ли в STL что-либо эквивалентное shared_ptr_comparator (определенному в программе выше)? Я спрашиваю, потому что компаратор, который я написал, кажется действительно распространенным случаем использования, и я был бы очень удивлен, если бы STL не имел ничего эквивалентного ему.

Я тоже удивлен, но нет, STL не имеет встроенного компаратора для указателей, который работает таким образом. Однако есть лучший способ реализовать его, который позволяет передавать любой указатель.
template<typename T, typename comp_t>
bool ptr_compare(T lhs, T rhs, comp_t comp) {
    return comp(*lhs, *rhs);
}

Вы можете назвать это так Способ:

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

И если вы хотите версию, совместимую с контейнерами STL:

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

Что именно делают owner_less и owner_before (которые он называет)? Они просто проверяют эквивалентность нижележащих указателей? Я не уверен, правильно ли я его использую.

std::owner_less сравнивается не по стоимости, а по владельцу, так что это не имеет отношения к вашей проблеме.

std::shared_ptr<T>::owner_before вызывается std::owner_less, чтобы выяснить порядок.