Почему сами функторы STL шаблонны, а не их оператор вызова функции?

Функторы STL реализованы следующим образом:

template<class T>
struct less{
  bool operator()(T const& lhs, T const& rhs){
    return lhs < rhs;
  }
};

Это заставляет нас упоминать тип (возможно, длинный) каждый раз, когда мы создаем такой функтор. Почему они не реализованы, как показано ниже? Есть причины?

struct less{
  template<class T>
  bool operator()(T const& lhs, T const& rhs){
    return lhs < rhs;
  }
};

Это сделало бы их пригодными для использования без упоминания (возможно, длинных) типов.


person Xeo    schedule 04.07.2011    source источник
comment
начиная с C++14, вы можете использовать прозрачные компараторы, которые предоставляют template<class T> std::less<T=void> с std::less<void>, содержащим шаблон operator< (и аналогично для другие функторы).   -  person TemplateRex    schedule 03.08.2014


Ответы (2)


Это также сделало бы невозможным их специализацию для пользовательских типов.

Они должны быть точкой настройки.


Подведем итоги обсуждения в комментариях:

Хотя технически возможно сделать так, как предлагает Xeo, стандарт языка не позволяет этого.

Очень сложно написать шаблон рабочего класса, если пользователям разрешено специализировать отдельные функции шаблона. Однако в некоторых случаях может быть хорошей идеей специализировать весь класс для определенного пользователем типа.

Поэтому стандарт C++98 пишет (17.4.3.1):

Для программы C++ не определено добавлять объявления или определения в пространство имен std или пространства имен в пределах пространства имен std, если не указано иное. Программа может добавить специализации шаблона для любого шаблона стандартной библиотеки в пространство имен std.

Поскольку не указано иное, что код Xeo разрешен, мы должны понимать, что это не так. Возможно, не совсем очевидно! Или что «шаблонные специализации» применимы только к классам.

В новом стандарте C++11 эта часть расширена и более подробно описана (17.6.4.2):

Поведение программы на C++ не определено, если она добавляет объявления или определения в пространство имен std или в пространство имен внутри пространства имен std, если не указано иное. Программа может добавить специализацию шаблона для любого шаблона стандартной библиотеки в пространство имен std только в том случае, если объявление зависит от определяемого пользователем типа и специализация соответствует требованиям стандартной библиотеки для исходного шаблона и не запрещена явно.

Поведение программы на C++ не определено, если она объявляет

— явная специализация любой функции-члена шаблона класса стандартной библиотеки, или
— явная специализация любого шаблона функции-члена стандартного библиотечного класса или шаблона класса, или
— явная или частичная специализация любого члена шаблон класса стандартного библиотечного класса или шаблон класса.

Программа может явно создавать экземпляр шаблона, определенного в стандартной библиотеке, только если объявление зависит от имени определяемого пользователем типа и создание экземпляра соответствует требованиям стандартной библиотеки для исходного шаблона.

person Bo Persson    schedule 04.07.2011
comment
Ну.. как насчет того, чтобы сделать свой собственный? В любом случае вам нужно создать новую структуру. - person Xeo; 05.07.2011
comment
На всякий случай, если то, что сказал Бо, трудно понять, вы можете определить шаблон ‹› bool less‹std::string›::operator()(...) {... } - person fulmicoton; 05.07.2011
comment
@poule: template<> bool std::less::operator()<std::string> ? Или я что-то упускаю? - person Xeo; 05.07.2011
comment
@Xeo Тогда вам всегда нужно указывать свой собственный функтор, тогда как less<your_type> можно вывести в большинстве мест. - person Konrad Rudolph; 05.07.2011
comment
Почему вы хотите, чтобы less<T> делал что-то отличное от operator<? Кажется, это действительно сбивает с толку. - person interjay; 05.07.2011
comment
Ваша собственная версия не будет иметь такое же имя;) Например, std::less — это тип функтора по умолчанию для сравнения ключей в std::map, поэтому, если вашему типу ключа нужен специальный тип, вы можете настроить std:: less, реализовав его для вашего конкретного типа. У вас есть другие способы решить этот конкретный случай, но дело в том, что точка настройки на самом деле означает сохранение того же имени, но использование определенной настроенной оптимизации для данного типа параметра. - person Klaim; 05.07.2011
comment
Кроме того, почему бы просто не определить bool operator< для вашего пользовательского типа? - person Xeo; 05.07.2011
comment
@Xeo - вам может не понадобиться оператор‹ для некоторых типов, но вы все равно сможете поместить их в набор или карту. Это просто вариант, который вы можете выбрать. - person Bo Persson; 05.07.2011
comment
@Xeo: будет ли это компилироваться, если некоторые cpp включают эту меньшую специализацию, а некоторые нет? (это действительно вопрос) - person fulmicoton; 05.07.2011
comment
@Bo: Хорошо, мой другой тезис о специализации оператора остается в силе. - person Xeo; 05.07.2011
comment
@poule: Да, так и есть, см. мой последний комментарий. Выглядит немного некрасиво. - person Xeo; 05.07.2011
comment
@›Xeo - Если оператор является шаблоном, я не могу его специализировать для MyType. Я также не могу передать оператор как параметр шаблона, но с std::less‹MyType› это возможно. - person Bo Persson; 05.07.2011
comment
@Бо: А? Я только что сделал специализацию в примере с Идеоне! Кроме того, вы можете просто передать только less в качестве параметра шаблона. - person Xeo; 05.07.2011
comment
@Xeo - Пожалуйста! Не может, как в не разрешено. Вам разрешено специализировать типы из пространства имен std. - person Bo Persson; 05.07.2011
comment
@Bo: Вам также разрешено специализировать функции. - person Xeo; 05.07.2011
comment
@Xeo - Нет, вы можете специализировать только шаблоны классов. С++ 98 говорит об этом в 17.4.3. С++ 11 явно говорит, что UB специфицирует функцию-член (17.6.4.2). - person Bo Persson; 05.07.2011
comment
@Bo: Мех, тогда нарушил правила. :‹ О, и один момент, который вы, возможно, захотите добавить, так как вы получили единственный ответ, момент, который я упустил из виду, это определения типов в binary_function, из которых less происходит. Определения типов были бы невозможны без шаблонного struct. - person Xeo; 05.07.2011
comment
@Xeo мой плохой, он компилируется на gcc. Я был уверен, что это запрещено. - person fulmicoton; 05.07.2011
comment
@Xeo Забавно, я получаю разные результаты при отдельной компиляции с флагами оптимизации и без флагов оптимизации. Я полностью понимаю, как задействовано встраивание, но мне интересно, будет ли то же самое с кодом моды STL. - person fulmicoton; 05.07.2011
comment
@BoPersson: Использовать вы должны только специфицировать шаблоны классов, а я должен не специфицировать их для MyType. :) - person Lightness Races in Orbit; 05.07.2011

Может быть:

std::multiset<long, std::less<int> > moduloset;

Странная вещь, но дело в том, что std::less<int>, std::less<long>, std::less<unsigned int> реализуют разные математические функции, которые дают разные результаты при передаче (результат преобразования) определенных выражений-аргументов. Различные алгоритмы и другие стандартные библиотечные компоненты работают, задавая функтор, поэтому мне кажется логичным, что существуют разные функторы для представления этих разных математических функций, а не просто разные перегрузки operator() на одном функторе.

Более того, функтор с шаблоном operator() не может быть Adaptable Binary Predicate, поскольку у него нет типов аргументов (аргумент может иметь любой тип). Итак, если бы std::less был определен так, как вы предлагаете, он не мог бы участвовать в вещах в <functional>.

Также в весьма спекулятивной заметке - std::less, вероятно, был разработан до того, как поддержка функций-членов шаблона стала широко распространенной, поскольку в документации SGI STL есть различные примечания, в которых говорится: «Если ваша реализация не поддерживает шаблоны-члены, то это не так. т доступен». Для такого простого компонента, я думаю, был бы стимул сделать что-то, что работает сегодня. Когда он существует, стандартизация могла удалить его в пользу чего-то другого, но стоило ли нарушать существующий код? Если бы это было так важно, то либо вы, либо стандарт могли бы ввести функтор flexible_less, как вы описываете.

Наконец, почему

template<class T>
bool operator()(T const& lhs, T const& rhs){
  return lhs < rhs;
}

скорее, чем

template<class T, class U>
bool operator()(T const& lhs, U const& rhs){
  return lhs < rhs;
}

Для определяемых пользователем типов эти два значения могут не совпадать. Да, это несправедливый вопрос, так как я не знаю, почему нет версии std::less с двумя шаблонами-аргументами ;-)

person Steve Jessop    schedule 04.07.2011
comment
так как я не знаю, почему нет версии std::less с двумя аргументами шаблона - Сделайте это новым вопросом! :П - person Xeo; 05.07.2011