С++ std::functional

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

Оригинал: Wikipedia: functional (C++)
Что делает функция bind() в коде

Функциональный объект

Функциональный объект, или функтор — это класс с определённым оператором вызова функции - operator () таким образом, объект класса:

class FunctionObjectType {
  public:
  void operator() () {/* Do some work */}
};

может быть использован следующим образом:

FunctionObjectType func;
func();

У использования функциональных объектов есть ряд преимуществ перед использованием функций, а именно:

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

Предикаты

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

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

std::function

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

Определение класса std::function

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 v) : val(v)
    {/* EMPTY */}

    void printMe(char c) const
    {
        std::cout << "Val: " << val
                  << " Char: " << c << "\n";
    }

    int val;
};

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

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

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

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

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

    // Содержит сылку на метод класса.
    std::function<void(const A&, char)> methodRef = &A::printMe;
    A a(10);

    // для вызова необходима ссылка на объект соотв. класса
    methodRef(a, 'A'); // Val: 10 Char: A

    // Содержит функциональный объект.
    B b;
    std::function<void()> functorRef = b;
    functorRef(); // 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::bind

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

Определение функции std::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), а также по одному объекту для каждого аргумента аналогичного типа std::decay<Arg_i>::type.

#include <functional>
#include <iostream>

using namespace std;

class Object {
public:
    void printMe(const int a, const std::string& s)
    {
        std::cout << "Hello " << s
                  << " aaa = " << a << '\n';
    }
};

void myFunc(const std::string &s, const int a)
{
    std::cout << "Goodbye " << s
              << " aaa = "  << a << '\n';
}

int main(int argc, char* argv[])
{
    typedef std::function<void(const std::string&, const int)> ExampleFunction;
    Object instance;

    ExampleFunction binder = std::bind(
        &Object::printMe,
        &instance,  // необходима ссылка на экземпляр объекта
        std::placeholders::_2,
        std::placeholders::_1
    );
    binder("World", 111);

    binder = std::bind(
        &myFunc,
        std::placeholders::_1, // поменял местами с 2
        std::placeholders::_2  // поменял местами с 1
    );
    binder("World", 222);

    auto f2 = std::bind(
        &myFunc,
        "foooo",
        std::placeholders::_1
    );
    f2(555);

    return 0;
}

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
}

Функциональные объекты

Набор предопределённых функциональных объектов для базовых операций был неотъемлемой частью стандартной библиотеки шаблонов с момента её появления в стандарте. Это базовые арифметические операции (+-*/%), базовые логические операции (&&, ||, !) и операции сравнения (==, !=, >, <, >=, <=). Несмотря на их тривиальность, используя именно эти классы проводилась демонстрация возможностей алгоритмов стандартной библиотеки. Также их наличие способствует удобству и избавляет пользователя библиотеки от избыточной работы по написанию собственных аналогов. Логические функторы и функторы сравнения являются предикатами и возвращают булевский тип. Начиная с С++11, также были добавлены некоторые битовые операции (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 соответственно, что предоставляло пользователю возможность использовать отрицатели для собственных предикатов. Так как упомянутые выше базовые классы были помечены устаревшими, а какой-либо замены отрицателям, помимо лямбда-функций, нет, то их решено было оставить.

Обёртки ссылок

В заголовочном файле <functional> определен небольшой вспомогательный класс std::reference_wrapper, который оборачивает в себе ссылку на объект, или ссылку на функцию, переданную ему в шаблоне. Он может быть полезен для передачи ссылок аблонам функций (к примеру, в алгоритмах), которые обычно делают копии объектов при передаче по значению. Всё, что делает reference_wrapper, это хранение ссылки на переданный в шаблоне тип T, и выдачу её при обращении operator T& ().

Для создания объектов 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;