Я хочу реализовать некоторые общие алгоритмы, и у меня есть ряд идей о том, как можно реализовать специализированные алгоритмы в зависимости от определенных признаков сущностей, с которыми используется алгоритм. Тем не менее, кажется вероятным, что я не придумал все специальные черты, и я хотел бы реализовать общую версию, чтобы они могли работать с другой специализированной версией.
Например, рассмотрим distance(begin, end)
(да, я знаю, что он находится в библиотеке standad, однако он приятный и простой и может быть использован для демонстрации моей проблемы). Общая версия может выглядеть так (я использую std::ptrdiff_t
вместо std::iterator_traits<It>::difference_type
как другое упрощение):
template <typename It>
auto distance(It it, It end) -> std::ptrdiff_t {
std::ptrdiff_t size{};
while (it != end) {
++it;
++size;
}
return size;
}
Конечно, если тип итератора является итератором с произвольным доступом, гораздо лучше реализовать алгоритм, используя разницу между двумя итераторами. Наивно просто добавляя
template <typename It>
auto distance(It begin, It end)
-> typename std::enable_if<is_random_access_v<It>, std::ptrdiff_t>::type {
return end - begin;
}
не совсем работает: обе реализации одинаково хороши для итераторов с произвольным доступом, т.е. компилятор считает их неоднозначными. Легкий подход к решению этой проблемы состоит в том, чтобы изменить общую реализацию, применимую только для итераторов без случайного доступа. То есть, выбор SFINAE сделан таким образом, что они являются взаимоисключающими, а также охватывают все пространство.
К сожалению, набор реализаций по-прежнему закрыт: без изменения подписи, по крайней мере, одной из реализаций я не могу добавить другую реализацию, если у меня есть другая идея для универсальной реализации, использующей специальные свойства. Например, если я хочу добавить специальную обработку для сегментированных диапазонов (идея: когда базовая последовательность состоит из сегментов как есть, например, для случая std::deque<...>
или std::istreambuf_iterator<cT>
, обрабатывать сегменты отдельно), необходимо будет изменить общая реализация должна применяться только тогда, когда последовательности не являются произвольным доступом и не являются сегментированной последовательностью. Конечно, если я контролирую выполнение, которое можно сделать. Пользователь не сможет расширять набор общих реализаций.
Я знаю, что функции могут быть перегружены для специальных типов итераторов. Однако для этого потребуется, чтобы каждый раз, когда добавлялся итератор со специальными возможностями, ему необходимо было реализовать соответствующие функции. Цель состоит в том, чтобы включить добавление универсальных реализаций, которые являются улучшением в случае, если объекты, которые они используют, предоставляют дополнительные возможности. Он похож на разные категории итераторов, хотя свойства ортогональны категориям итераторов.
Таким образом, мой вопрос:
- Можно ли реализовать универсальные алгоритмы, чтобы можно было добавить новую идею улучшения без изменения существующих реализаций, и если да, то как?
- Дополнительное наблюдение (меня в первую очередь интересует вопрос выше, но это тоже может быть интересно): Если это невозможно, добавилась бы эта способность с концепциями?