Упражнение 14.43. Используя библиотечные объекты функций, определите, делимо ли переданное значение типа int на некий элемент в контейнере целых чисел.

<p>14.8.3. Вызываемые объекты и тип <code>function</code></p>

В языке С++ есть несколько видов вызываемых объектов: функции и указатели на функции, лямбда-выражения (см. раздел 10.3.2), объекты, созданные функцией bind() (см. раздел 10.3.4), и классы с перегруженным оператором вызова функции.

Подобно любому другому объекту, у вызываемого объекта есть тип. Например, у каждого лямбда-выражения есть собственный уникальный (безымянный) тип класса. Типы функций и указателей на функции зависят от типа возвращаемого значения, типа аргумента и т.д.

Однако два вызываемых объекта с разными типами могут иметь ту же сигнатуру вызова (call signature). Сигнатура вызова определяет тип возвращаемого значения вызываемого объекта и тип (типы) аргумента, которые следует передать при вызове. Сигнатура вызова соответствует типу функции. Например:

int(int, int)

Функция этого типа получает два числа типа int и возвращает значение типа int.

Разные типы могут иметь одинаковую сигнатуру вызова

Иногда необходимо использовать несколько вызываемых объектов с одинаковой сигнатурой вызова, как будто это тот же тип. Рассмотрим, например, следующие разные типы вызываемых объектов:

// обычная функция

int add(int i, int j) { return i + j; }

// лямбда-выражение, создающее безымянный класс объекта функции

auto mod = [](int i, int j) { return i % j; };

// класс объекта функции

struct div {

 int operator()(int denominator, int divisor) {

  return denominator / divisor;

 }

};

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

int(int, int)

Эти вызываемые объекты можно использовать для написания простого калькулятора. Для этого следует определить таблицу функций (function table), хранящую "указатели" на вызываемые объекты. Когда программе понадобится выполнить некую операцию, она просмотрит таблицу и найдет соответствующую функцию.

В языке С++ таблицы функций довольно просто реализовать при помощи карт (map). В данном случае как ключ используем строку, соответствующую символу оператора; значение будет функцией, реализующей этот оператор. При необходимости выполнить заданный оператор индексируется карта и осуществляется вызов возвращенного элемента. Если бы все эти функции были автономными и необходимо было использовать только парные операторы для типа int, то карту можно было бы определить так:

// сопоставляет оператор с указателем на функцию, получающую два целых

// числа и возвращающую целое число

map binops;

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

// ok: add - указатель на функцию соответствующего типа

binops.insert({"+", add}); // {"+", add} - пара раздел 11.2.3

Но сохранить в карте binops объекты mod или div не получится:

binops.insert({"%", mod}); // ошибка: mod - не указатель на функцию

Проблема в том, что mod — это лямбда-выражение, и у каждого лямбда-выражения есть собственный тип класса. Этот тип не соответствует типу значений, хранимых в карте binops.

Библиотечный тип function

Эту проблему можно решить при помощи нового библиотечного типа function, определенного в заголовке functional; возможные операции с типом function приведены в табл. 14.3.

Таблица 14.3. Операции с типом function

Перейти на страницу:

Похожие книги