Что такое прозрачные компараторы?


в C++14 ассоциативные контейнеры, похоже, изменились с C++11 – [ассоциативный.reqmts] / 13 говорит:

шаблоны функций-членов find,count,lower_bound,upper_bound и equal_range не участвует в разрешении перегрузки, если тип .

какова цель сделать компаратор "прозрачным"?

C++14 также предоставляет шаблоны библиотек, как это:

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

так для например, std::set<T, std::less<T>> б не есть прозрачный компаратор, но std::set<T, std::less<>>б есть один.

какую проблему это решает, и это меняет, как работают стандартные контейнеры? Например, параметры шаблона std::set по-прежнему Key, Compare = std::less<Key>, ..., так что набор по умолчанию теряет свой find,count и т. д. участники?

4 85

4 ответа:

какую проблему это решит,

см. ответ Дитмара и ремябеля.

и это меняет то, как работают стандартные контейнеры?

нет, не по умолчанию.

новый шаблон функции-члена перегружает find etc. позволяет использовать тип, сопоставимый с ключом контейнера, вместо использования самого типа ключа. Смотрите N3465 по настоящее время Хоакин Лопес Муньос для обоснование и подробное, тщательно написанное предложение добавить эту функцию.

на встрече в Бристоле РГПВ согласилась с тем, что функция гетерогенного поиска является полезной и желательной, но мы не могли быть уверены, что предложение Хоакина будет безопасным во всех случаях. Предложение N3465 вызвало бы серьезные проблемы для некоторых программ (см. влияние на существующий код). Хоакин подготовил обновленный проект предложения с некоторыми альтернативными реализациями различные компромиссы, которые были очень полезны, помогая LWG понять плюсы и минусы, но все они рисковали каким-то образом нарушить некоторые программы, поэтому не было консенсуса по добавлению этой функции. Мы решили, что хотя было бы небезопасно добавлять эту функцию безоговорочно, было бы безопасно, если бы она была отключена по умолчанию и только "включилась".

ключевое отличие N3657 предложение (которое было пересмотрено в последнюю минуту мной и STL на основе N3465 и более поздний неопубликованный проект Хоакина) должен был добавить is_transparent введите в качестве протокола, который может быть использован для выбора новой функциональности.

если вы не используете "прозрачный функтор" (т. е. то, что определяет is_transparent type), то контейнеры ведут себя так же, как они всегда делали, и это по-прежнему по умолчанию.

если вы решите использовать std::less<> (что является новым для C++14) или другого типа "прозрачный функтор", то вы получаете новый функциональность.

используя std::less<> легко с шаблонами псевдоним:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

имя is_transparent приходит от STL N3421 который добавил "алмазные операторы" в C++14. "Прозрачный функтор" - это тот, который принимает любые типы аргументов (которые не должны быть одинаковыми) и просто передает эти рассуждения к другому оператору. Такой функтор оказывается именно тем, что вы хотите для гетерогенного поиска в ассоциативных контейнерах, поэтому тип is_transparent был добавлено ко всем алмазным операторам и используется в качестве типа тега, чтобы указать, что новая функциональность должна быть включена в ассоциативных контейнерах. Технически, контейнеры не нуждаются в "прозрачном функторе", только тот, который поддерживает вызов его с гетерогенными типами (например,pointer_comp введите https://stackoverflow.com/a/18940595/981959 не является прозрачным в соответствии с определением STL, но определяет pointer_comp::is_transparent позволяет использовать его для решения проблемы). Если вы только когда-либо поиск в вашем std::set<T, C> С ключами типа T или int затем C только должен быть вызван с аргументами типа T и int (в любом порядке), он не должен быть по-настоящему прозрачным. Мы использовали это имя отчасти потому, что мы не могли придумать лучшего имени (я бы предпочел is_polymorphic потому что такие функторы используют статический полиморфизм, но уже есть std::is_polymorphic тип признака, который относится к динамическому полиморфизму).

в C++11 нет шаблонов членов find(),lower_bound() и т. д. То есть от этого изменения ничего не теряется. Шаблоны элементов были введены с n3657, чтобы разрешить использование гетерогенных Ключей с ассоциативными контейнерами. Я не вижу никакого конкретного примера, где это полезно, за исключением примера, который хорош и плох!

The is_transparent использование предназначено для предотвращения нежелательных преобразований. Если шаблоны элементов не были ограничены, существующий код может проходить через объекты непосредственно, который был бы преобразован без шаблонов членов. Пример использования-чехол из n3657 местонахождение объекта в std::set<std::string> использование строкового литерала: с определением C++11 a std::string объект строится при передаче строковых литералов в соответствующую функцию-член. С изменением можно использовать строковый литерал напрямую. Если базовый объект функции сравнения реализован исключительно в терминах std::string это плохо, потому что теперь std::string будет создается для каждого сравнения. С другой стороны, если базовый объект функции сравнения можно взять std::string и строковый литерал, который может избежать построения временного объекта.

вложенный элемент is_transparent тип в объекте функции сравнения предоставляет способ указать, следует ли использовать шаблонную функцию-член: если объект функции сравнения может работать с разнородными аргументами, он определяет этот тип, чтобы указать, что он может работать с разными аргументами эффективно. Например, новые объекты, функции оператора просто делегировать operator<() и претендовать на прозрачность. Это, по крайней мере, работает std::string который перегружен меньше, чем операторы, принимающие char const* в качестве аргумента. Поскольку эти функциональные объекты также являются новыми, даже если они делают неправильные вещи (т. е. требуют преобразования для некоторого типа), это, по крайней мере, не будет тихим изменением, приводящим к снижению производительности.

ниже все копии-паста от n3657.

вопрос:какова цель сделать компаратор "прозрачным"?

A. ассоциативные функции поиска контейнера (find, lower_bound, upper_bound, equal_range) принимает только аргумент key_type, требующий пользователи, чтобы построить (неявно или явно) объект key_type для выполнения поиска. Это может быть дорого, например, строительство большой объект для поиска в наборе, когда функция компаратора только смотрит на одно поле объекта. Есть сильное желание среди пользователей чтобы иметь возможность поиска с использованием других типов, которые сопоставимы с тип ключа.

вопрос:какую проблему это решит

A. У LWG были проблемы с кодом следующим образом:

std::set<std::string> s = /* ... */;
s.find("key");

В C++11 это создаст один std:: string временный, а затем сравниваем его с элементами найти ключи.

С изменением, предложенным N3465, функция std::set::find() будет быть неограниченным шаблоном, который будет передавать const char* через для функции компаратора, std:: меньше, что бы постройте временную строку std::для каждого сравнения. РГПВ рассматривал эту проблему производительности как серьезную проблему. Этот шаблон найти() функция также будет препятствовать установлению нуль в контейнер указателей, который приводит к отсутствию ранее действительного кода длиннее компиляции, но это было воспринято как менее серьезная проблема, чем молчать регрессия производительности

вопрос:это меняет то, как работают стандартные контейнеры

А. Это предложение изменяет ассоциативные контейнеры В и перегружая функции-члены поиска с помощью функции-члена шаблоны. Языковые изменения отсутствуют.

вопрос:таким образом, набор по умолчанию теряет свою находку, подсчет и т. д. участники

A. почти весь существующий код C++11 не затронут, потому что член функции отсутствуют, если не используются новые функции библиотеки C++14 как функции сравнения.

цитата Якк,

В C++14, std:: set:: find является функцией шаблона, если Сравните::is_transparent существует. Тип, который вы передаете в не нужно будьте ключом, просто эквивалентным под вашим компаратором.

и n3657,

добавить пункт 13 в 23.2.4 [ассоциативность.потребности]: Шаблоны функций-членов find, lower_bound, upper_bound и equal_range не участвует в разрешении перегрузки, если тип сравнения:: is_transparent не существует существует.

n3421 пример "Прозрачные Операторные Функторы".

на полный код здесь.

Stephan T Lavavej рассказывает о проблемах, когда компилятор продолжает создавать временные функции, и как его предложение прозрачных операторных функторов решит это в c++1y

GoingNative 2013 - не помогайте компилятору (примерно в час)