Если я не ошибаюсь, когда-то давно видел код c темплейтами, который во время компиляции выполнял алгоритм решета эратосфена на массиве константной длины. Можете подсказать как подобное делается или поделиться ссылкой? Было бы еще лучше инициализировать константный массив подобным образом, типа statiс const int arr[42];
на самом деле задача проще, нужно проинциализировать счетчик числа битов a[i] = a[i >> 1] + (i & 1)
Это называется шаблонное метапрограммирование. Примеры кода можно найти тут.
"Шаблонне метапрограмування". Спасибо. Но я уже рассмотрел несколько нагугленных примеров, даже инициализирующих массив, но либо криво, либо не константный.
Массив инициализировать вряд ли получится, тогда уж можно просто макросами писать. То, что получится — это вместо массива сделать шаблон, параметризуемый
int
, и потом специализировать его для нужных значений. Тогда обращение к «массиву» будет выглядеть какmy_array<42>::value()
.вот кривой пример
Ага, я это видел в гугле. Только вот преобразование
const CC<N> *
->const int *
и последующее разыменование этого указателя вызывает undefined behavior. Такое прокатит только если вместоint
взятьchar
/unsigned char
, да и тогда не факт, что будет работать правильно.нужно выравнивание задать прагмой, тогда нормально всё. но код кривой. и еще я сомневаюсь что это будет компайл тайм.
offtop: в последней scala (2.10) ввели механизм макросов, с помощью которого можно выполнять на этапе компиляции практически любой код
Хмм, а на codeforces ведь есть скала, только более старая... В связи с этим интересно было бы услышать time-limit/memory-limit для компиляции :)
В связи с этим интересно было бы услышать когда обновят скалу :)
Это я смог получить в запуске. Какая-то странная константа.
Я думаю это то, сколько ты запросил, а не сколько разрешено, так что ничего странного
И аналогичный вопрос — с языком D.
Когда я разбирался с возможностями C++, у меня получилось вот это: examples
Конкретно код, который во время компиляции считает все простые до 403: primes
P.S. На практике возможности предподсчета на C++ во время компиляции не применимы. Слишком медленно работает.
Когда-то тоже с этим разбирался и нашел одно полезное применение.
Когда мы пишем быстрое преобразование Фурье, то самым лучшим алгоритмом по критерию простота написания/скорость работы является рекурсивный вариант вида:
На всем известной задаче решение с таким написанием Фурье проходит более чем с двукратным запасом по тайм-лимиту.
Однако даже это решение можно простым способом ускорить примерно на 20%. Можно написать эту функцию как шаблонную, тогда, насколько мне известно, рекурсия будет разворачиваться на этапе компиляции и на это будет тратиться меньше времени:
При такой реализации функцию нужно вызывать следующим образом:
Естественно, минус такого подхода в том, что мы заранее должны знать длину массива, к которому применять функцию. Однако, если в задаче ограничения известны заранее и нет мультитеста — можно всегда вызывать с максимально возможной длиной. Если же есть мультитест, то не обойтись без switch, или кучи ифов :)
Такое Фурье по-прежнему совсем несложно писать, но работает оно действительно существенно быстрее.
Действительно классно.
Жаль, что я уже нерекурсивно пишу. Это после Харьковского контеста на Фурье от Димы Жукова :-) Получается вроде тоже достаточно коротко: fourier
P.S. Интересно было бы такую же идею применить, например, к дереву отрезков с запросом сверху (хотя бы для малых N <= 2^10).
UPD: Попробовал переписать "дерево отрезков с операциями sum и +=" на шаблоны для N <= 2^8. Компилится секунду. Зато стало работать в (!) 5 раз быстрее. Круто. Вот код: старый новый.
UPD2: "В 5 раз быстрее" достигается при использовании MinGW g++ 4.7.2 на core i5. При использовании Visual C++ 2005 и 2010 Express у меня получилось только "в 1.8 раз быстрее"
Ну в качестве совсем жесткого пихания можно так раскрывать сколько-то верхних уровней. Или наоборот нижних.
А здесь BrainFuck на шаблонах, что заодно доказывает тьюринг-полноту :)
С тьюринг-полнотой все плохо из-за наличия в компиляторах жесткого ограничения на глубину раскрытия :)
А как же
-ftemplate-depth=n
в GCC? :-) GCC, пожалуй, ограничен только доступным количеством памяти.Ну, с Тьюринг-полнотой, наверное, везде все плохо из-за отсутвия бесконечной памяти. Просто тут на практике предел слишком легко достигается.
Отвечая сразу на два оффтопных комментария, замечу, что в языке D можно запустить почти любую обычную функцию на этапе компиляции. Конечно, функция должна удовлетворять некоторым условиям.
Пример: решение задачи 237C с вычислением количеств простых чисел во время выполнения и во время компиляции. Разница в двух строчках: объявим мы эти массивы как глобальные константы (immutable) или как переменные на стеке.
Конечно, это только в теории всё отлично — на практике есть несколько проблем. Из того, что я уже видел: интерпретатор, который выполняет compile-time evaluation, на пару порядков медленнее и более неуклюже работает, чем скомпилированный вариант функции. Так что ресурсоёмкий предподсчёт компилятору не скормить — у него быстро кончается время или память. Ну и конкретно в этой задаче, конечно, от вычисления таблицы простых на этапе компиляции (которое, кстати, занимает порядка десяти секунд!) решение быстрее не стало (фактически стало даже медленнее: 125 ms против 109 ms, но это вполне вписывается в погрешность при измерении времени).