Год назад, прочитав в
C++ Template Metaprogramming про возможность явно управлять включением/невключением функции в overload set при помощи
boost::enable_if, мысленно отнёс этот трюк к категории вещей типа «а вот гляди, братва, как я могу» - прикольных, но в реальной жизни не нужных. Оказалось, нужных, и ещё как.
Это нужно всегда, когда мы упираемся в отсутствие в C++ concept-based overloading. Хочется надеяться, что
когда-нибудь оно-таки появится, но на данный момент язык не предоставляет готовых средств перегрузить функцию так, чтобы она вызывалась только для типов, удовлетворяющих некоему (произвольно сложному) заданному условию.
Я в это упёрся вчера при прикручивании к старому приложению новой функциональности. У приложения есть помойк свалк сложносочинённая структура данных глубокой вложенности и разнообразия, и нужно её сериализовать в текстовый файл в виде набора CSV-таблиц. Задача простая, но есть нюанс. Файл должен быть удобен для восприятия и редактирования человеком, поэтому к сериализации каждой структуры нужно подходить индивидуально. Как, например, выглядит в общем случае сериализация map-ы? Итерируем по всем элементам и рекурсивно сериализуем ключ, а потом значение. И то, и другое, в свою очередь, может быть произвольно сложной структурой, а значит каждый ключ и каждое значение становятся на выходе набором таблиц, в лучшем случае - одной таблицей. А если мапа простая, типа map? Общий алгоритм превратит её в длинную серию n*2 таблиц из одного элемента каждая, хотя тут явно просится одна-единственная двухстолбцовая таблица из n строк. Значит, нам нужно перегрузить функцию сериализации для тех мапов, у которых и ключ, и значение являются числами.
Встроенными языковыми средствами эту задачу можно решить только перегружая функцию отдельно для каждой возможных пары численных типов, что даст нам квадратичный копи-пейст.
А вот так выглядит решение с boost::enable_if:
template
typename enable_if,is_arithmetic > >::type
SerializeAsCsv( ostream &os, const map& m )
{
// Customized serialization
}Синтакс, конечно, жутковат, как и у всего темплейтного в C++, но зато исходную идею о том, в каких случаях должна быть применена данная специализация, получилось донести до компилятора в первозданном, естественном и читабельном (ну, понятно, в пределах читабельности темплейтного кода на C++ :) виде. А что может быть лучшей характеристикой качества кода?