Что такое прозрачные компараторы?
В C++14 ассоциативные контейнеры, по-видимому, изменились по сравнению с C++11 - [associative.reqmts]/13 говорит:
Шаблоны функций-членов
find
,count
,lower_bound
,upper_bound
, а такжеequal_range
не должны участвовать в разрешении перегрузки, если типCompare::is_transparent
существует.
Какова цель сделать компаратор "прозрачным"?
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 ответа
Какую проблему это решает,
Смотри Дитмар и ремиабель ответ.
и это меняет, как работают стандартные контейнеры?
Нет, не по умолчанию.
Новый шаблон функции-члена перегружает find
и т.д. позволяют использовать тип, сопоставимый с ключом контейнера, вместо использования самого типа ключа. См. N3465 от Joaquín Mª López Muñoz для обоснования и подробного, тщательно написанного предложения по добавлению этой функции.
На встрече в Бристоле LWG согласилась, что функция гетерогенного поиска была полезной и желательной, но мы не могли быть уверены, что предложение Хоакина будет безопасным во всех случаях. Предложение N3465 вызвало бы серьезные проблемы для некоторых программ (см. Раздел " Влияние на существующий код "). Хоакин подготовил обновленный проект предложения с некоторыми альтернативными реализациями с различными компромиссами, который был очень полезен, помогая LWG понять плюсы и минусы, но все они рисковали каким-то образом нарушить некоторые программы, поэтому не было единодушия относительно добавления этой функции. Мы решили, что хотя добавление этой функции безоговорочно и небезопасно, было бы безопасно, если бы она была отключена по умолчанию и только "включила".
Ключевым отличием предложения N3657 (которое было в последнюю минуту пересмотрено мной и STL на основе N3465 и позднее неопубликованным проектом Хоакина) было добавление is_transparent
введите в качестве протокола, который может использоваться, чтобы включить новые функции.
Если вы не используете "прозрачный функтор" (то есть тот, который определяет is_transparent
тип), тогда контейнеры ведут себя так же, как они всегда делали, и это по-прежнему по умолчанию.
Если вы решите использовать 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
происходит от ST34 N3421, который добавил "операторы алмазов" в C++14. "Прозрачный функтор" - это тот, который принимает любые типы аргументов (которые не должны быть одинаковыми) и просто перенаправляет эти аргументы другому оператору. Такой функтор оказывается именно тем, что вам нужно для гетерогенного поиска в ассоциативных контейнерах, поэтому тип is_transparent
был добавлен ко всем операторам diamond и использовался в качестве типа тега, чтобы указать, что новая функциональность должна быть включена в ассоциативных контейнерах. Технически, контейнерам не нужен "прозрачный функтор", только тот, который поддерживает вызов его с гетерогенными типами (например, pointer_comp
введите /questions/6519255/poisk-neobrabotannyih-ukazatelej-dlya-naborov-uniqueptrs/6519275#6519275 не прозрачный в соответствии с определением 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, чтобы разрешить использование разнородных ключей с ассоциативными контейнерами. Я не вижу конкретного примера, где это полезно, кроме примера, который хорош и плох!
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.
В. Какова цель сделать компаратор "прозрачным"?
О. Функции поиска ассоциативного контейнера (find, lower_bound, upper_bound, equal_range) принимают только аргумент key_type, требуя от пользователей создания (неявного или явного) объекта типа key_type для выполнения поиска. Это может быть дорого, например, создание большого объекта для поиска в наборе, когда функция компаратора просматривает только одно поле объекта. Пользователи очень хотят иметь возможность поиска с использованием других типов, которые сопоставимы с key_type.
В. Какую проблему это решает?
О. У LWG были проблемы с кодом, подобным следующему:
std::set<std::string> s = /* ... */; s.find("key");
В C++11 это создаст одну временную строку std::string и затем сравнит ее с элементами, чтобы найти ключ.
С изменением, предложенным N3465, функция std::set::find() будет неограниченным шаблоном, который будет передавать const char * через функцию компаратора std::less, которая создаст временную строку std::string для каждое сравнение LWG сочла эту проблему производительности серьезной проблемой. Функция шаблона find () также предотвращает поиск значения NULL в контейнере указателей, что приводит к тому, что ранее действительный код больше не компилируется, но это рассматривается как менее серьезная проблема, чем тихая регрессия производительности.
Q. это меняет работу стандартных контейнеров
О. Это предложение изменяет ассоциативные контейнеры и перегружает функции-члены поиска шаблонами функций-членов. Там нет языковых изменений.
Q. Так что набор по умолчанию теряет свои члены find, count и т. Д.
О. Почти весь существующий код C++11 остается неизменным, потому что функции-члены отсутствуют, если только новые функции библиотеки C++14 не используются в качестве функций сравнения.
Процитирую Якка,
В C++14 std:: set:: find является функцией шаблона, если существует Compare::is_transparent. Тип, который вы передаете, не обязательно должен быть ключевым, просто эквивалентным вашему компаратору.
и n3657,
Добавить пункт 13 в 23.2.4 [associative.reqmts]: шаблоны функций-членов find, lower_bound, upper_bound и equal_range не должны участвовать в разрешении перегрузки, если тип Compare::is_transparent
не существует ине существует.
В n3421 приведен пример "Прозрачные операторные функторы".
Стефан Т Лававей рассказывает о проблемах, когда компилятор продолжает создавать временные объекты, и о том, как его предложение прозрачных функторов операторов решит эту проблему в C++ 1y
GoingNative 2013 - Не помогите компилятору (примерно в час)