C++ / Магия шаблонов или вычисление факториала на стадии компиляции

Apr 29, 2011 23:01



C++ / Магия шаблонов или вычисление факториала на стадии компиляции
Доброго времени суток, Хабралюди!

Гуру C++, а также люди смыслящие в шаблонном метапрограммировании могут смело пропускать этот топик, ничего нового для себя они здесь не найдут. Однако, если после прочтения заголовка, у вас в голове еще не возникло решение данной задачи (и даже если оно возникло, но не при помощи шаблонов), то милости просим под кат.

Собственно, решение задачи:

#include

template
class Factorial {
public:
static const int f = Factorial::f * n;
};

template<>
class Factorial<0> {
public:
static const int f = 1;
};

int main() {
std::cout << Factorial<5>::f << std::endl; // 120
}

Давайте разберемся, что к чему. Во-первых, некоторые могут спросить - а почему значение вычисляется на стадии компиляции? Ответ - это основа шаблонов в C++. Специализированный код генерируется на стадии компиляции, в зависимости от того каким типом реально был параметризован шаблон. Суть в том, что если вы используете, например, шаблон функции, которая возвращает минимум двух значений:

template const T& min(const T& a, const T& b) {
return (a < b) ? a : b;
}

То если использовать в программе, например:

min(2, 3)

То сгенерируется только специализация для типа int. А если написать, что-то вроде:

min(2.0, 3.0)

Или

min(2.0, 3)

То сгенерируется специализация для double.

Теперь, возвращаясь к нашему факториалу, можно понять, что для генерации шаблона Factorial<5> должен сгенерироваться шаблон Factorial<4> и так далее до нуля, где в Factorial<0>::f записывается просто единица (за счет явной специализации template<>). Этот последний шаг останавливает «рекурсивную» генерацию шаблонов, после чего вычисляется само значение факториала. Почему на стадии компиляции? Потому что константа static const int f является константой времени компиляции. Если кто-то не верит в это, можете задать какое-нибудь значение шаблона в качестве длины массива, и наблюдать спокойную компиляцию программы.

В книге Брюса Эккеля Философия С++. Практическое программирование. Приводится следующее решение этой задачи (которое по сути ничем не отличается от вышеописанного):

template
struct {
enum {val = Factorial * n};
};

template<>
struct Factorial<0>{
enum {val = 1};
};

Вообще, такое вычисление факториала является частным случаем шаблонного метапрограммирования. Например, возведение в степень q, целого числа p, можно было бы написать циклом:

int power = 1;
while (q--)
power *= p;

Но также можно и при помощи шаблонного метапрограммирования:

template
class Power {
public:
static const int value = p * Power
::value;
};

template
class Power
{
public:
static const int value = 1;
}

Более подробно об этом можно прочесть, например, у Эккеля, в книге Философия С++. Практическое программирование, или у Александреску в книге Современное проектирование на C++.

Спасибо за внимание!
Original source: habrahabr.ru (comments).
Previous post Next post
Up