STL Функциональный - Почему?

В C++ Standard Template Library есть "функциональная" часть, в которой многие классы перегружены () оператор.

Приносит ли это удобство использования функций в качестве объектов в C++?

Почему мы не можем просто использовать указатель на функцию вместо этого? Есть примеры?

4 ответа

Решение

Конечно, всегда можно использовать указатели на функции вместо функциональных объектов, однако существуют определенные преимущества, которые функциональные объекты предоставляют по сравнению с указателями на функции, а именно:

  • Лучшая производительность:

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

  • Функциональные объекты являются интеллектуальными функциями:

Функциональные объекты могут иметь другие функции-члены и атрибуты. Это означает, что функциональные объекты имеют состояние. Фактически, одна и та же функция, представленная объектом функции, может одновременно иметь разные состояния. Это невозможно для обычных функций. Еще одним преимуществом объектов функций является то, что вы можете инициализировать их во время выполнения, прежде чем использовать / вызывать их.

  • Сила общего программирования:

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

Почему мы не можем просто использовать указатель на функцию вместо этого? Есть примеры?

Использование указателя функции в стиле C не может использовать преимущество встраивания. Указатель на функцию обычно требует дополнительной косвенности для поиска.
Однако если operator () перегружен, тогда компилятору очень легко inline код и сохранить дополнительный вызов, поэтому увеличение производительности.

Другое преимущество перегружено operator () в том, что можно разработать функцию, которая неявно рассматривает объект функции как аргумент; нет необходимости передавать его как отдельную функцию. Меньше ручная программа, меньше ошибок и лучшая читаемость.

Этот вопрос с веб-страницы Бьярна Страуструпа (изобретателя C++) прекрасно объясняет этот аспект.

C++ Standard (Template) Library использует функциональное программирование с перегруженным operator (), если это необходимо.

> Приносит ли какое-либо удобство использование функций в качестве объектов в C++?

Да: механизм шаблонов C++ допускает все остальные стили программирования C/C++ (стиль C и стиль ООП, см. Ниже).

> Почему мы не можем просто использовать указатель на функцию вместо этого? Есть примеры?

Но мы можем: Простой указатель на функцию C- это тоже объект с четко определенным оператором (). Если мы проектируем библиотеку, мы не хотим заставлять кого-либо использовать этот стиль указателя Си, если это не нужно. Обычно это так же нежелательно, как принуждать все / всех быть в / использовать стиль ООП; увидеть ниже.


С точки зрения C-программистов и функциональных программистов, ООП имеет тенденцию быть не только медленным, но и более многословным и в большинстве случаев быть неправильным направлением абстракции ("информация" не является и не должна быть "объектом"). Из-за этого люди, как правило, путаются, когда слово "объект" используется в других контекстах.

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


Чтобы понять различия в производительности, сравните стили / парадигмы программирования (-языка) и их возможные оптимизации:

С стиль:

  • Указатель на функцию с ее замыканием ("this" в ООП, указатель на некоторую структуру) в качестве первого параметра.
  • Чтобы вызвать функцию, необходимо сначала обратиться к ее адресу.
  • Это 1 косвенность; встраивание невозможно.

Стиль ООП в C++ (и Java):

  • Ссылка на объект, производный от класса с виртуальными функциями.
  • Ссылка 1-й указатель.
  • Указатель на виртуальную таблицу является вторым указателем.
  • Указатель функции в виртуальной таблице является третьим указателем.
  • Это 3 направления; встраивание невозможно.

Стиль шаблона C++:

  • Копирование объекта с функцией ().
  • Нет виртуальной таблицы, так как тип этого объекта известен во время компиляции.
  • Адрес функции известен во время компиляции.
  • Это 0 указателей; возможно встраивание.

Шаблоны C++ достаточно универсальны, чтобы разрешить другие два стиля выше, и в случае встраивания они могут даже превзойти…

скомпилированные функциональные языки: (исключая JVM и Javascript в качестве целевых платформ из-за отсутствия "правильных вызовов")

  • Указатель на функцию и ссылка на ее закрытие в регистрах машины.
  • Обычно это не функция "вызов", а GOTO-подобный прыжок.
  • Функциям не нужен стек, нет адреса для возврата назад, нет параметров или локальных переменных в стеке.
  • Функции имеют свои закрываемые закрытия, содержащие параметры, и указатель на следующую функцию, которая будет вызвана.
  • Чтобы процессор мог предсказать скачок, адрес функции должен быть загружен в регистр как можно раньше.
  • Это 1 косвенное направление с возможным прогнозом скачка; все почти так же быстро, как встроено.

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

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

Другие вопросы по тегам