functional (C++)
Functional — заголовочный файл в стандартной библиотеке языка программирования C++, предоставляющий набор шаблонов классов для работы с функциональными объектами, а также набор вспомогательных классов для их использования в алгоритмах стандартной библиотеки.
История
[править | править код]Впервые заголовочный файл <functional> появился в стандарте языка в 1998 году[1], куда был добавлен вместе со стандартной библиотекой шаблонов. Изначально в него вошел набор вспомогательных функциональных объектов для удобства использования алгоритмов STL. Также сюда вошли связыватели (binders) и набор обёрток функций, цель которых была облегчить работу в тех случаях, когда активно использовалась передача указателей на функции, то есть работа с функциями, как с некими объектами.[2] Существенное пополнение заголовочного файла предлагалось в библиотеке расширений C++ TR1[3] . Из библиотеки Boost в STL переносились такие классы, как function, bind, mem_fn, result_of, reference_wrapper, hash. Большинство этих изменений, за исключением result_of, и вошло в актуальный на данный момент стандарт языка C++17[4]. Поскольку классы 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
}
};
У использования функциональных объектов есть ряд преимуществ[5] перед использованием функций, а именно:
- Функциональный объект может иметь состояние. Фактически может быть два объекта одного и того же функционального типа, находящиеся в разных состояниях в одно и тоже время, что невозможно для обычных функций. Также функциональный объект может обеспечить операции предварительной инициализации данных.
- Каждый функциональный объект имеет тип, а следовательно имеется возможность передать этот тип как параметр шаблона для указания определённого поведения. К примеру, типы контейнеров с разными функциональными объектами отличаются.
- Объекты-функции зачастую выполняются быстрее чем указатели на функции. К примеру, встроить (inline) обращение к оператору () класса легче, чем функцию, переданную по указателю[6].
Предикаты
[править | править код]Функциональные объекты, которые возвращают булевский тип, называются предикатами. В стандартной библиотеке используются унарные и бинарные предикаты. Поведение предиката не должно зависеть от количества выполненных операций копирования над этим предикатом, поскольку стандарт C++ не определяет, сколько раз предикат может быть скопирован при использовании в алгоритмах. Иными словами, для того, чтобы пользовательский предикат был приемлемым для STL, он не должен менять своё состояние при копировании или вызове.
Обёртки функций
[править | править код]std::function
[править | править код]Начиная со стандарта C++11 шаблонный класс std::function является полиморфной обёрткой функций для общего использования. Объекты класса std::function могут хранить, копировать и вызывать произвольные вызываемые объекты — функции, лямбда-выражения, выражения связывания и другие функциональные объекты. Говоря в общем, в любом месте, где необходимо использовать указатель на функцию для её отложенного вызова, или для создания функции обратного вызова, вместо него может быть использован std::function, который предоставляет пользователю большую гибкость в реализации.
Впервые данный класс появился в библиотеке Function в версии Boost 1.23.0[7]. После его дальнейшей разработки он был включен в стандарт расширения 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 создаёт объект-обёртку вокруг указателей на члены класса. Этот объект может хранить, копировать и вызывать член класса по указателю. В качестве указателя могут также использоваться ссылки и умные указатели[8].
Впервые шаблонная функция std::mem_fn появилась в библиотеке Member Function в версии Boost 1.25.0[7]. Она также была включена в 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, в частности, пользовательские предикаты полезны, когда необходимо воспользоваться алгоритмами для классов, разработанных не на основе стандартной библиотеки[6].
Однако, адаптивный функциональный протокол, базирующийся на наследовании, который вводили эти классы, был заменен лямбда-функциями и std::bind в C++11[9], и стало накладно поддерживать этот протокол для новых компонентов библиотеки. Кроме того, избавление от наследования разрешало некоторые неоднозначности[10]. Поэтому было решено обозначить эти классы как устаревшие в C++11[4].
Устаревшие адаптеры
[править | править код]В стандарте имеются адаптеры указателей на функцию и адаптеры методов классов, которые в стандарте 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[7]. Там она позиционировалась как обобщение и расширение стандартных связывателей 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
Устаревшие связыватели
[править | править код]В редакции стандарта C++ 1998 года стандартная библиотека предоставляла связыватели std::bind1st и std::bind2nd, позволяющие функцию от двух аргументов преобразовать в функцию от одного аргумента, привязывая второй аргумент к какому-либо значению. На вход они принимают функциональный объект и значение аргумента для связывания, а возвращают шаблонные классы std::binder1st и std::binder2nd, наследники unary_function, соответственно.
Пример использования.
void func(list<int>& cont)
{
list<int>::const_iterator iter = find_if(cont.begin(), cont.end(), bind2nd(greater<int>(), 10));
// Do some work ...
}
Функциональные объекты
[править | править код]Набор предопределённых функциональных объектов для базовых операций был неотъемлемой частью стандартной библиотеки шаблонов с момента её появления в стандарте[2]. Это базовые арифметические операции (+-*/%), базовые логические операции (&&, ||, !) и операции сравнения (==, !=, >, <, >=, <=). Несмотря на их тривиальность, используя именно эти классы проводилась демонстрация возможностей алгоритмов стандартной библиотеки. Также их наличие способствует удобству и избавляет пользователя библиотеки от избыточной работы по написанию собственных аналогов[6]. Логические функторы и функторы сравнения являются предикатами и возвращают булевский тип. Начиная с C++11[4], также были добавлены некоторые битовые операции (and, or, xor, not).
Тип | Название | Кол-во операндов | Возвращаемый тип | Действие |
---|---|---|---|---|
Сравнения | equal_to | Бинарный | bool | x == y |
not_equal_to | Бинарный | bool | x != y | |
greater | Бинарный | bool | x > y | |
less | Бинарный | bool | x < y | |
greater_equal | Бинарный | bool | x >= y | |
less_equal | Бинарный | bool | x <= y | |
Логические | logical_and | Бинарный | bool | x && y |
logical_or | Бинарный | bool | x || y | |
logical_not | Унарный | bool | !x | |
Арифметические | plus | Бинарный | T | x + y |
minus | Бинарный | T | x - y | |
multiplies | Бинарный | T | x * y | |
divides | Бинарный | T | x / y | |
modulus | Бинарный | T | x % y | |
negate | Унарный | T | -x | |
Битовые (C++11) | bit_and | Бинарный | T | x & y |
bit_or | Бинарный | T | x | y | |
bit_xor | Бинарный | T | x ^ y | |
bit_not | Унарный | T | ~x |
Отрицатели (negators)
[править | править код]Также, наряду с предопределёнными предикатами, в заголовочном файле имеются отрицатели предикатов, которые вызывают предикат и возвращают результат обратный результату предиката. Предикатные отрицатели сродни связывателям в том, что они принимают операцию и производят из неё другую операцию. Библиотека предоставляет два таких отрицателя: унарный not1() и бинарный not2(). Возвращаемым типом этих отрицателей есть специальные вспомогательные классы unary_negate и binary_negate, определенные следующим образом:
template <class Predicate> class unary_negate {
public:
explicit unary_negate(const Predicate& pred);
bool operator()(const typename Predicate::argument_type& x) const;
};
template <class Predicate> class binary_negate {
public:
explicit binary_negate(const Predicate& pred);
bool operator()(const typename Predicate::first_argument_type& x, const typename Predicate::second_argument_type& y) const;
Здесь operator() возвращает !pred(x) в первом случае, и !pred(x,y) во втором. Унарный предикат должен иметь определенный тип argument_type, а бинарный — типы first_argument_type и second_argument_type. Наличие таких определений у таких классов как std::function, std::mem_fn и std::ref делает возможным использование отрицателей вместе с обёртками функций.
В изначальной редакции стандарта unary_negate и binary_negate наследовались от базовых классов unary_function и binary_function соответственно, что предоставляло пользователю возможность использовать отрицатели для собственных предикатов. Так как упомянутые выше базовые классы были помечены устаревшими, а какой-либо замены отрицателям, помимо лямбда-функций, нет[11], то их решено было оставить.
Обёртки ссылок
[править | править код]В заголовочном файле <functional> определен небольшой вспомогательный класс std::reference_wrapper, который оборачивает в себе ссылку на объект, или ссылку на функцию, переданную ему в шаблоне. Он может быть полезен для передачи ссылок шаблонам функций (к примеру, в алгоритмах), которые обычно делают копии объектов при передаче по значению. Всё, что делает reference_wrapper, это хранение ссылки на переданный в шаблоне тип T, и выдачу её при обращении operator T& ().
Впервые шаблонный класс reference_wrapper появился в библиотеке Ref в версии Boost 1.25.0[7]. С некоторыми доработками он был включен в C++11.
Для создания объектов reference_wrapper предоставлены вспомогательные функции ref и cref, определённые следующим образом:
template <class T> reference_wrapper<T> ref(T& t) noexcept;
template <class T> reference_wrapper<const T> cref(const T& t) noexcept;
См. также
[править | править код]Примечания
[править | править код]- ↑ Programming languages - C++ (англ.). ISO/IEC 14882 (23 апреля 1998). Дата обращения: 1 мая 2013. Архивировано 17 мая 2013 года.
- ↑ 1 2 Alexander Stepanov and Meng Lee. The Standard Template Library (англ.). HP Laboratories Technical Report 95-11(R.1) (14 ноября 1995). Дата обращения: 1 мая 2013. Архивировано 17 мая 2013 года.
- ↑ Draft Technical Report on C++ Library Extensions (англ.) : journal. — ISO/IEC JTC1/SC22/WG21, 2005. — 24 June. Архивировано 14 апреля 2011 года.
- ↑ 1 2 3 ISO/IEC 14882:2017 . ISO (2 сентября 2011). Дата обращения: 2 мая 2013. Архивировано 17 мая 2013 года.
- ↑ Josuttis, Nicolai M. The C++ standard library : a tutorial and reference (англ.). — Addison-Wesley, 2012. — ISBN 0-321-62321-5.
- ↑ 1 2 3 Stroustrup, Bjarne. The C++ Programming Language: Special Edition (англ.). — Addison-Wesley, 2000. — ISBN 0-201-70073-5.
- ↑ 1 2 3 4 Boost Library Documentation (англ.). Дата обращения: 1 мая 2013. Архивировано 17 мая 2013 года.
- ↑ Boost Library Documentation : mem_fn.hpp (англ.). Дата обращения: 2 мая 2013. Архивировано 17 мая 2013 года.
- ↑ C++ FCD Comment Status :GB95 (англ.). Дата обращения: 3 мая 2013. Архивировано 17 мая 2013 года.
- ↑ Deprecating unary_function and binary_function (англ.). Дата обращения: 3 мая 2013. Архивировано 17 мая 2013 года.
- ↑ Deprecating unary_function and binary_function (Revision 1) (англ.). Дата обращения: 3 мая 2013. Архивировано 17 мая 2013 года.
Ссылки
[править | править код]- Описание Functional на сайте cppreference.com
- Заголовочный файл Functional на сайте MSDN
- Boost.Function
Для улучшения этой статьи желательно:
|