Следующий фрагмент возвращает следующую наивысшую степень двух для (предполагаемого беззнакового) целого числа типа T. Он делает это путем многократного сдвига битов.
Для всех намерений и целей беззнаковый тип i, используемый в цикле сдвига битов, является достаточно большим, чтобы представлять (номинально) 65536 битовых чисел. Таким образом, практически можно оставить все как есть.
template <class T>
T supremum_2(T k) {
if (k == T(0)) return T(1);
k--;
for (int i=1; i<sizeof(T)*8; i++) k |= k >> i;
return k+1;
}
Чтобы выполнить профессиональную работу, тип счетчика циклов следует выбирать во время компиляции, чтобы он гарантировал возможность расширения до sizeof (T) * 8 без переполнения.
Можно ли это сделать во время компиляции, используя std :: numeric_traits? Если так, то как?
Концептуально я хотел бы написать что-то вроде:
typedef unsigned_type_that_can_represent<sizeof(T)*8> counter_type;
...
...
for (counter_type i(1); i<sizeof(T)*8; i<<=1) k = k | k >> i;
...
На основании приведенных ниже обсуждений я решил добавить следующий контекст.
Другими словами:
Как мы можем гарантировать выбор эффективных (только таких больших, как они должны быть) и подходящих типов для внутренней работы кода шаблона во время компиляции? Если мы обнаружим, что используем конкретные типы в коде шаблона, мы можем делать непреднамеренные предположения о типах шаблонов через потенциально непрозрачный маршрут.
Например, если мы будем придерживаться (скажем) значения int для счетчика, все будет работать нормально, пока кто-то не использует код шаблона в своей библиотеке bigint. Это может представлять целые числа с большим количеством двоичных цифр, чем может быть представлено int. Должны ли мы поэтому сделать тип unsigned long long? Конечно, это только задерживает проблему (хотя и надолго)? В этом решении есть оттенки «640K - достаточно большие для всех» или статические размеры массивов.
Очевидный, но несколько неэффективный выбор в этом случае состоит в том, чтобы установить тип счетчика таким же, как тип числа k. Это (в принципе) неэффективно, так как мы только требуем, чтобы счетчик мог представлять число, соответствующее количеству битов k. Может случиться так, что в других ситуациях это неправильно.
А как насчет общего случая? Похоже, что метапрограммирование является подходящим подходом. Как сохранить это в здравом уме? Возможно, формально требование состоит в том, чтобы функция времени компиляции отображала (потенциально производные) требования абстрактных типов на типы.
Возможно, это работа для YABL (еще одна библиотека повышения)!
[Извинения за бессвязные слова]