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

История Впервые заголовочный файл <functional> появился в стандарте языка в 1998 году, куда был добавлен вместе со стандартной библиотекой шаблонов. Изначально в него вошел набор вспомогательных функциональных объектов для удобства использования алгоритмов STL. Также сюда вошли связыватели (binders) и набор обёрток функций, цель которых была облегчить работу в тех случаях, когда активно использовалась передача указателей на функции, то есть работа с функциями, как с некими объектами. Существенное пополнение заголовочного файла предлагалось в библиотеке расширений C++ TR1 . Из библиотеки Boost в STL переносились такие классы, как function, bind, mem_fn, result_of, reference_wrapper, hash. Большинство этих изменений, за исключением result_of, и вошло в актуальный на данный момент стандарт языка C++17. Поскольку классы function и bind во многом дублируют функциональность связывателей и обёрток функций редакции стандарта 1998 года, то в C++11 последние были обозначены как устаревшие (deprecated).

Основные понятия

Термины стандарта В документе стандарта языка C++11 вводятся следующие термины касаемо классов заголовочного файла <functional>. Тип функционального объекта (function object type) — тип объекта, который может быть типом постфиксного выражения в вызове функции, где постфиксное выражение — набор перегруженных функций, или шаблонов функций, или адрес такого набора, или функциональный объект. Сигнатура вызова (call signature) — это название возвращаемого типа за которым следует в круглых скобках список нуля или более типов аргументов. Вызываемый тип (callable type) — это или тип функционального объекта, или указатель на член класса. Вызываемый объект (callable object) — это объект вызываемого типа. Тип обёртки вызова (call wrapper type) — это тип, который содержит вызываемый объект, и поддерживает операцию вызова, которая ведет к вызову (invoke) хранимого объекта. Обёртка вызова (call wrapper) — объект типа обёртки вызова. Целевой объект (target object) — вызываемый объект, который содержит обёртка вызова.

Понятие функционального объекта Функциональный объект, или функтор — это класс с определённым оператором вызова функции — operator () таким образом, что в следующем коде FunctionObjectType func; func(); выражение func() является вызовом operator() функционального объекта func, а не вызовом некоторой функции с именем func. Тип функционального объекта должен быть определён следующим образом: class FunctionObjectType { public: void operator() () { // Do some work

У использования функциональных объектов есть ряд преимуществ перед использованием функций, а именно: Функциональный объект может иметь состояние. Фактически может быть два объекта одного и того же функционального типа, находящиеся в разных состояниях в одно и тоже время, что невозможно для обычных функций. Также функциональный объект может обеспечить операции предварительной инициализации данных. Каждый функциональный объект имеет тип, а следовательно имеется возможность передать этот тип как параметр шаблона для указания определённого поведения. К примеру, типы контейнеров с разными функциональными объектами отличаются. Объекты-функции зачастую выполняются быстрее чем указатели на функции. К примеру, встроить (inline) обращение к оператору () класса легче, чем функцию, переданную по указателю.

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

Обёртки функций

std::function Начиная со стандарта C++11 шаблонный класс std::function является полиморфной обёрткой функций для общего использования. Объекты класса std::function могут хранить, копировать и вызывать произвольные вызываемые объекты — функции, лямбда-выражения, выражения связывания и другие функциональные объекты. Говоря в общем, в любом месте, где необходимо использовать указатель на функцию для её отложенного вызова, или для создания функции обратного вызова, вместо него может быть использован std::function, который предоставляет пользователю большую гибкость в реализации.

Впервые данный класс появился в библиотеке Function в версии Boost 1.23.0. После его дальнейшей разработки он был включен в стандарт расширения C++ TR1 и окончательно утверждён в C++11.

Определение класса template<class> class function; // undefined template<class R, class... ArgTypes> class function<R(ArgTypes...)>; Также в стандарте определены вспомогательные модификаторы swap и assign и операторы сравнения (== и !=) с nullptr. Доступ к целевому объекту предоставляет функция target, а к его типу — target_type. Оператор приведения function к булевскому типу возвращает true, когда у класса есть целевой объект.

Пример использования #include <iostream> #include <functional>

struct A { A(int num) : num_(num){} void printNumberLetter(char c) const {std::cout << "Number: " << num_ << " Letter: " << c << std::endl;} int num_;

void printLetter(char c) { std::cout << c << std::endl; }

struct B { void operator() () {std::cout << "B()" << std::endl;}

int main() { // Содержит функцию. std::function<void(char)> f_print_Letter = printLetter; f_print_Letter('Q');

// Содержит лямбда-выражение. std::function<void()> f_print_Hello = [] () {std::cout << "Hello world!" << std::endl;}; f_print_Hello();

// Содержит связыватель. std::function<void()> f_print_Z = std::bind(printLetter, 'Z'); f_print_Z();

// Содержит вызов метода класса. std::function<void(const A&, char)> f_printA = &A::printNumberLetter; A a(10); f_printA(a, 'A');

// Содержит функциональный объект. B b; std::function<void()> f_B = b; f_B(); } Результатом работы приведённого выше кода будет: Q Hello world! Z Number: 10 Letter: A B()

std::bad_functional_call Исключение типа bad_functional_call будет брошено при попытке вызова обёртки функции function::operator(), если у этой обёртки отсутствует целевой объект. bad_functional_call наследуется от std::exception, и у него доступен виртуальный метод what() для получения текста ошибки. Пример использования: #include <iostream> #include <functional>

int main() { std::function<void()> func = nullptr; try { func(); } catch(const std::bad_function_call& e) { std::cout << e.what() << std::endl;

std::mem_fn Шаблонная функция std::mem_fn создаёт объект-обёртку вокруг указателей на члены класса. Этот объект может хранить, копировать и вызывать член класса по указателю. В качестве указателя могут также использоваться ссылки и умные указатели.

Впервые шаблонная функция std::mem_fn появилась в библиотеке Member Function в версии Boost 1.25.0. Она также была включена в C++ TR1 и окончательно в C++11. В библиотеке Boost она разрабатывалась как обобщение стандартных функций std::mem_fun и std::mem_fun_ref.

Устаревшие базовые классы До включения в C++11 частей библиотеки Boost, в стандартной библиотеке были свои аналоги обёрток функций. В помощь для написании объектов-функций, библиотека предоставляет следующие базовые классы. template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; Назначение этих классов — дать стандартные имена типам аргументов и возвращаемых значений для избавления от путаницы в дальнейшем использовании пользовательских предикатов. Пользовательские предикаты, в свою очередь, позволяют просто и изящно пользоваться контейнерами и алгоритмами STL, в частности, пользовательские предикаты полезны, когда необходимо воспользоваться алгоритмами для классов, разработанных не на основе стандартной библиотеки.

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

Устаревшие адаптеры В стандарте имеются адаптеры указателей на функцию и адаптеры методов классов, которые в стандарте C++11 объявлены устаревшими, поскольку они дублируют функциональность нововведений.

std::ptr_fun позволяет создавать обёртки вокруг функций от одного и двух аргументов. Одно из применений — передача глобальных функций, обёрнутых этим адаптером, алгоритмам STL. Типом возвращаемого значения являются шаблонные классы std::pointer_to_unary_function или std::pointer_to_binary_function в зависимости от количества аргументов.

Связыватели

std::bind Шаблонная функция std::bind называется связывателем и предоставляет поддержку частичного применения функций. Она привязывает некоторые аргументы к функциональному объекту, создавая новый функциональный объект. То есть вызов связывателя эквивалентен вызову функционального объекта с некоторыми определёнными параметрами. Передавать связывателю можно или непосредственно значения аргументов, или специальные имена, определенные в пространстве имен std::placeholders, которые указывают связывателю на то, что данный аргумент не будет связан, и определяют порядок аргументов у возвращаемого функционального объекта.

Впервые данная функция появилась в библиотеке Bind в версии Boost 1.25.0. Там она позиционировалась как обобщение и расширение стандартных связывателей std::bind1st и std::bind2nd, так как позволяла связывать произвольное количество аргументов и изменять их порядок. В редакции стандарта C++11 bind был включен в библиотеку и предыдущие связыватели были обозначены устаревшими.

Определение функции template<class F, class... BoundArgs> unspecified bind(F&& f, BoundArgs&&... bound_args); template<class R, class F, class... BoundArgs> unspecified bind(F&& f, BoundArgs&&... bound_args); Здесь f — вызываемый объект, bound_args — список связанных аргументов. Возвращаемым значением есть функциональный объект неопределённого типа T, который может быть помещён в std::function, и для которого выполняется std::is_bind_expression<T>::value == true. Внутри обёртка содержит объект типа std::decay<F>::type, построенного с std::forward<F>(f), а также по одному объекту для каждого аргумента аналогичного типа std::decay<Arg_i>::type.

std::placeholders В пространстве имён std::placeholders содержатся специальные объекты _1, _2, ... , _N, где число N зависит от реализации. Они используются в функции bind для задания порядка свободных аргументов. Когда такие объекты передаются в виде аргументов в функцию bind, то для них генерируется функциональный объект, в котором, при вызове с несвязанными аргументами, каждый заполнитель _N будет заменён на N-й по счёту несвязанный аргумент.

Для получения целого числа k из заполнителя _K предусмотрен вспомогательный шаблонный класс std::is_placeholder. При передаче ему заполнителя, как параметра шаблона, есть возможность получить целое число при обращении к его полю value. К примеру, is_placeholder<_3>::value вернёт 3.

Пример #include <iostream> #include <functional>

int myPlus (int a, int b) {return a + b;}

int main() { std::function<int (int)> f(std::bind(myPlus, std::placeholders::_1, 5)); std::cout << f(10) << std::endl; } Результатом работы этого примера будет: 15

Устаревшие связыватели