// (4) Member merthod
run(CallbackConverter(&Executor::callbackHandler , &executor));
// (6) lambda-expression
run([capturedValue](int eventID) {/*it will be called by initiator*/});
Алгоритмы – краеугольный камень информатики, они встречаются практически во всех ее разделах. Таким образом, проектирование и разработка алгоритмов – одна из важнейших задач как в теоретической науке, так и в инженерной практике.
В реализации алгоритмов одной из трудностей, встающей перед разработчиком, является адаптация для конкретной структуры данных. Это связано с тем, что алгоритмы задают последовательность операций, но не определяют данные, с которыми работают. Предполагается, что алгоритм работает с любой структурой данных.
Например, предположим, что мы написали код для алгоритма сортировки. Естественно предположить, что он будет сортировать числа. Но вот появилась новая задача: отсортировать строки. По сравнению с исходной реализацией у нас теперь другая структура данных (строки) и новые правила сравнения (строки сравниваются совсем не так, как числа). А ведь в будущем, возможно, появятся более сложные случаи – например, сортировка структур по отдельным полям… Как написать универсальный код, работающий с любыми типами данных?
4.3.2. Параметризация типов
Обозначенная выше проблема в рамках параметрического полиморфизма решается просто: код оформляется в виде шаблона, параметрами шаблона выступают типы данных. При инстанциировании шаблона генерируется код, в который подставляются соответствующие типы.
Поясним сказанное на примере. Предположим, мы реализовали алгоритм сортировки пузырьком (Листинг 31).
Листинг 31. Сортировка массива методом пузырька
void sort_bubble(int* data, size_t size)
{
for (size_t i = 0; i < size – 1; i++)
{
for (size_t j = 0; j < size – i – 1; j++)
{
if (data[j + 1] < data[j])
{
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
Описанный код работает с числами. Параметризуем типы (Листинг 32):
Листинг 32. Параметризация типов для сортировки пузырьком
template // (1)
void sort_bubble(Data* data, size_t size) // (2)
{
for (size_t i = 0; i < size – 1; i++)
{
for (size_t j = 0; j < size – i – 1; j++)
{
if (data[j + 1] < data[j])
{
Data temp = data[j]; // (3)
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
По сравнению с предыдущим листингом изменений здесь совсем немного: в строке 1 объявлен параметр шаблона для типа данных, в реализации функции вместо типа данных подставляется параметр шаблона (строки 2 и 3). Теперь мы можем делать сортировку для любого типа данных: мы просто вызываем функцию и передаем ей требуемую переменную-массив, а компилятор сгенерирует код для соответствующего массива.
4.3.3. Объявление предикатов
После описанной модификации первоначального кода у нас остается одна проблема: как выполнять операции сравнения для нечисловых данных, например, структур? Ведь алгоритм не знает, да и не должен знать, по каким правилам нужно их сравнивать. Выход очевидный – делегировать эти операции создателю данных. Для этого будем использовать обратный вызов «вычисление по запросу» (п. 1.2.2). Параметрами вызова будут экземпляры данных, а возвращать он будет результат сравнения. Оформленный таким образом вызов называется предикатом.
Предикат– это выражение, принимающее одну или более величину и возвращающее результат булевого типа.
Объявим предикат как дополнительный параметр шаблона (Листинг 33).
Листинг 33. Шаблон с объявлением предиката
template // (1)
void sort_bubble(Data* data, size_t size, Predicate less) // (2)
{
for (size_t i = 0; i < size – 1; i++)
{
for (size_t j = 0; j < size – i – 1; j++)
{
if (less (data[j + 1], data[j])) // (3)
{
Data temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
По сравнению с предыдущим кодом из Листинг 32 изменения здесь следующие: в объявлении шаблона (строка 1) объявлен дополнительный параметр – предикат, в функции шаблона (строка 2) предикат объявляется как дополнительный входной параметр, в строке 3 вместо операции сравнения происходит вычисление предиката.
В качестве предикатов могут использоваться:
Читать дальше