return addr1.number < addr2.number;
else
return false;
}
}
};
int main()
{
int eventID = 0;
FO fo;
auto lambda = [](int eventID) { return 0; };
AddressDistributor, int(int)> distributor; // (3)
distributor.addReceiver({ 1,1 }, fo); // (4)
distributor.addReceiver({ 2,2 }, ExternalHandler); // (5)
distributor.addReceiver({ 3,3 }, lambda); // (6)
distributor({ 1,1 }, eventID); // (7)
distributor({ 2,2 }, eventID); // (8)
distributor({ 3,3 }, eventID); // (9)
}
В строке 1 объявлена структура для адреса, которая состоит из двух полей: идентификатор группы и номер получателя в группе. Сравнить эти две структуры напрямую нельзя, поэтому потребуется реализовать предикат.
В строке 2 объявлен функциональный объект, реализующий предикат для сравнения адресов. Почему именно в таком виде? Дело в том, что std::mapтребует, чтобы в качестве предиката использовался именно функциональный объект, мы не можем для этого использовать внешнюю функцию или лямбда-выражение. Это связано с тем, что в контейнере предикат хранится в виде переменной с конструктором, тип переменной определяется параметром шаблона. А наличие конструктора может обеспечить только функциональный объект.
Указанный подход имеет как достоинства, так и недостатки. С одной стороны, нам достаточно всего лишь объявить тип объекта в параметре шаблона, а затем про него можно забыть. Объект не нужно ни настраивать, ни передавать в конструктор как входной аргумент. С другой стороны, было бы удобно использовать в качестве предиката что-либо другое, например, лямбда-выражение или внешнюю функцию. Но в этом случае предикат пришлось бы инициализировать в конструкторе, причем ему нельзя было бы назначить значение по умолчанию. В любом случае, мы вынуждены следовать заданной реализации, поэтому предикат объявляем как функциональный объект.
В STL уже объявлен шаблон структуры для предикатов std::less, параметром которого выступает тип данных, которые необходимо сравнить. Этот предикат принимает на вход две переменные и возвращает true, если первая меньше второй 34 34 Контейнер std::map требует именно такой предикат, less, который возвращает истину в случае, если первый элемент меньше второго. Другие контейнеры могут требовать иные предикаты, например, проверку на равенство equal.
. std::lessреализует арифметическое сравнение, поэтому для типов, которые поддерживают арифметические операции, предикат объявлять не нужно, он будет сгенерирован компилятором. Однако в нашем случае данные арифметически сравниваться не могут, поэтому мы специализируем этот шаблон своим типом (строка 2) и реализуем перегруженный оператор, который будет сравнивать две структуры. При инстанциировании контейнера компилятор сам выберет подходящую специализацию предиката, исходя из типа хранимых элементов.
В строке 3 объявлен объект распределителя путем инстанциирования соответствующего шаблона. Аргументами шаблона выступают тип адреса, предикат для сравнения и сигнатура для вызова объектов. В строках 4, 5, 6 в распределитель добавляются объекты вызова различных типов, в строках 7, 8, 9 эти объекты будут вызваны в соответствии с их адресами.
Под распределением вызовов понимается техника, в которой при вызове единственной функции осуществляется выполнение множества вызовов через соответствующие аргументы. Структурно распределение состоит из следующих компонентов: источник, получатель, распределитель, распределяющая функция.
Если типы и количество получателей известны на этапе компиляции и не планируется их изменение в процессе выполнения программы, то мы имеем статический набор получателей. Распределитель для статического набора можно реализовать в виде функции, в этом случае распределитель структурно совпадает с распределяющей функцией.
В общем случае распределяющая функция принимает набор объектов и набор данных вызова. Эти наборы могут упаковываться в кортеж и пакет параметров в различных комбинациях. С точки зрения дизайна каждый способ упаковки имеет свои преимущества и недостатки, с точки зрения эффективности они равноценны.
Если требуются результаты выполнения вызовов, то они реализуются с помощью отдельной распределяющей функции, которая возвращает результаты в виде кортежа.
Зачастую бывает удобно реализовать распределитель для статического набора в виде класса, в котором объекты вызова хранятся в кортеже, а распределяющей функцией выступает перегруженный оператор. Здесь возникает проблема, как использовать класс с возвратом результатов выполнения и без возврата: перегруженный оператор имеет одинаковый набор входных параметров, различается только наличие и отсутствие возвращаемого значения. Выходом будет реализация двух отдельных классов либо общий класс с дополнительным параметром – индикатором. Во втором случае теряется возможность автоматического вывода типа.
Читать дальше