|
Программирование >> Разработка устойчивых систем
<Generative Programming: Methods, Tools, and Applications, Addison-Wesley, 2(Ю0, с. 417. tempiate<1nt N> struct Power<N. 0> { enum {val - 1}: int ma1n() { cout Power<2. 5>::val endl: 32 } III:- Условие остановки должно задаваться неполной специализацией, поскольку значение N остается свободным параметром шаблона. Помните, что программа работает только для неотрицательных показателей степени. Следующая метапрограмма, позаимствованная из книги Чарнецки (Chamecki) и Эйзенекера (Eisenecker), интересна тем, что в ней используется шаблон в качестве параметра шаблона и имитируется передача функции как параметра другой функции, которая перебирает числа0..п: : С05:Accumulate.Срр Передача функции как паранетра на стадии конпиляции #include <iostream> using namespace std: Накопление результатов F(0)..F(n) tempiate<lnt n. tempiate<int> class F> struct Accumulate { enum {val - Accumulate<n-1. F>:,-val + F<n>:;val}: Критерий остановки (возвращает значение F(0)) tempiate<template<int> class F> struct Accumulate<0. F> { enum (val - F<0>::val}: Различные функции : tempiate<int n> struct Identity { enum (val - n}: tempiate<int n> struct Square { enum {val - n*n}; tempiate<int n> struct Cube { enum {val - n*n*n}: int mainO { cout Accumu1ate<4. Identity>::val endl; 10 cout Accumu1ate<4. Square>::val endl: 30 cout Accumulate<4. Cube>::val endl: ICQ } III:- Основной шаблон Accumulate вычисляет сумму ¥{n) + F{n -1)... f(0). Критерий остановки определяется неполной реализацией, которая возвращает F(0). Параметр F сам по себе является шаблоном и работает как функция (по аналогии с предыдущими примерами этого раздела). Шаблоны Identity, Square и Cube вычисляют соответственно значение своего параметра, его квадрат и куб. Первая специализация Accumulate в main() вычисляет 4 + 3 + 2 + 1, поскольку шаблон Identity просто возвращает свой параметр. Вторая строка main() суммирует квадраты этих чисел (16 + 9 + 4 + 1+0), а третья - их третьи степени (64 + 27 + 8+ 1 + 0). Развертывание цикла Разработчики алгоритмов всегда стремились оптимизировать свои программы. Одной из классических оптимизаций в области математического программирования является развертывание цикла - методика, сокращающая издержки на выполнение циклов. Наиболее типичным примером развертывания цикла является матричное умножение. Следующая функция умножает матрицу на вектор (предполагается, что константы ROWS и COLS определены): void mult(int a[rows][cols]. int x[cols]. int y[cols]) { for (int i = 0: i < rows: ++i) { yCi] = 0: for (int j = 0; j < cols: y[i] += a[i][j]*x[j]: Если значение COLS четно, то издержки на увеличение и сравнение счетчика j можно сократить наполовину, развертывая вычисления во внутреннем цикле: void mult(int a[rows][cols]. int x[cols]. int y[cols]) { for (i nt i = 0: i < rows: ++i) { y[i] = 0: for (int j = 0: j < cols: j += 2) y[i] += a[i][j]*x[j] + a[i][j+l]*x[j+l]: В общем случае, если значение COLS кратно к, при каждой итерации внутреннего цикла можно выполнять к операций, что существенно повышает эффективность цикла. Конечно, экономия заметна только для очень больших массивов, но именно такие объемы данных обрабатываются современными математическими системами. Подстановка функций тоже может рассматриваться как своего рода разновидность развертывания циклов. Рассмотрим следующий способ вычисления степеней целых чисел: ; С05:Unroll.срр Развертывание неявного цикла путем подстановок linclude <iostream> using namespace std: tempiate<int n> inline int power(int m) { return power<n-l>(m) * m: templateo inline int power<l>(int m) { return m: tempi ateo inline int power<0>(int m) { return 1: int mainO { Существует гораздо более эффективный способ вычисления степеней целых чисел, который называется алгоритмом русского крестьянина . int m = 4; cout power<3>(ni) endl; } III:- Формально компилятор должен генерировать три специализации шаблона powero, по одной для каждого параметра 3, 2 и 1. Поскольку код каждой из этих функций может подставляться, фактически в main() будет вставлено единственное выражение m*m*m. Таким образом, простая специализация шаблона в сочетании с подстановкой позволяет полностью избавиться от издержек управления циклом. Эта разновидность развертывания цикла ограничивается максимальной глубиной подстановки в вашем компиляторе. Условный выбор на стадии компиляции Чтобы имитировать условные конструкции на стадии компиляции, можно воспользоваться условным тернарным оператором в объявлении enum. В следующей программе эта методика применяется для вычисления на стадии компиляции большего из двух целых чисел: : COS:Мах.срр linclude <iostream> using namespace std: tempiate<int nl. int n2> struct Max { enum {val = nl > n2 ? nl : n2}: int mainO { cout Max<10. 20>::val endl: 20 } III:- Чтобы в зависимости от условия компилятор генерировал разный код, используйте специализации с параметрами true и false: : COS:Conditionals.срр Применение условий стадии компиляции для выбора кода linclude <iostream> using namespace std; tempiate<bool cond> struct Select {}; templateo struct Select<true> { static inline void statementlO { cout This is statementl executingXn ; public: static inline void fO { statementlO; } templateo struct Select<false> { static inline void statement2() { cout This is statement2 executingXn : public: static inline void fO { statement2(); }
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |