Обновление: на Reddit мне сказали, что это старая перспектива программирования на C ++ и оптимизации компилятора. Если вы знакомы с метапрограммированием шаблонов и хотите получать последние обновления, перейдите к комментариям к сообщению Reddit.

Что такое метапрограммирование шаблонов?

« Под метапрограммированием понимается множество способов, которыми программа познает себя или может манипулировать собой». читает Википедию. Когда я впервые прочитал его, будучи начинающим аспирантом, я был поражен возможностью программ ИИ, способных писать другие фрагменты кода и захватывать мир. Это голливудская версия, но вот реалистичная, которая используется в отрасли для оптимизации времени компиляции с использованием шаблонов C ++.

Я столкнулся с этой концепцией во время исследования побочного проекта, связанного с оптимизацией. Хотя это понятие не далеко от основ курса CS101, оно не совсем хорошо известно, и, следовательно, блог не может его разбить.

Давайте для начала посмотрим, что такое шаблоны:
Шаблоны - это основа общего программирования, которое предполагает написание кода таким образом, чтобы он не зависел от какого-либо конкретного типа переменной.

Возьмем, к примеру, следующий код:

#include <iostream>
using namespace std;

// One function works for all data types.  
template <typename T>
T tMax(T x, T y) 
{ return (x > y)? x: y; }

int main()
{
cout << tMax<int>(3, 7) << endl; // Call myMax for type int
cout << tMax<double>(3.0, 7.0) << endl; // call myMax for type d
cout << tMax<char>(’g’, 'e’) << endl; // call myMax for type char
return 0;
}

Шаблон tMax предоставляет универсальную функцию, которая может использоваться всеми типами данных.

Как работают шаблоны?

Шаблоны компилируются в реальные типы ассемблером

Сами шаблоны не существуют как универсальные после компиляции программы, то есть в коде сборки.

Как и в примере tMax шаблона, который мы видели выше, шаблон tMax преобразуется ассемблером в разные функции для каждого из типов, вызываемых в коде.
ref: Assembly Code (строка 38 определяет функцию int tMax ‹int› (int, int), которая является tMax для типа int)

Вы можете перейти к Учебному руководству (шаблоны C ++), чтобы потратить некоторое время на шаблоны.
Конструкция, эквивалентная шаблонам, предоставляемым Java, - Generics.

Использование шаблонов для оптимизации

Поскольку шаблоны «разворачиваются» в функции во время компиляции, а компилятор также выполняет вычисления, в которых переменные не задействованы непосредственно во время компиляции, некоторые вычисления могут быть выполнены во время компиляции и их можно избежать во время выполнения как результат.

Вычисления можно завершить во время компиляции и избежать во время выполнения

Допустим, вы пишете фрагмент кода и хотите использовать значение 15! (15 факториал) как часть кода.
Рекурсивный способ сделать это - использовать следующий код:

long factorial(int n)
{
  if (n == 0)
    return 1;
  else
    return(n * factorial(n-1));
}
int main()
{
  cout << factorial(15) << endl;
  return 0;
}

Это компилируется в функцию, и вызов этой функции с аргументом n = 15 будет выполнен во время выполнения.
ref: Assembly Code (вы можете увидеть функцию factorial и call factorial (int) в строке 17)

Давайте теперь конвертируем в программу с помощью шаблонов.

template <long N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0> 
{
    enum { value = 1 };
};
int main()
{
    cout << Factorial<15>::value << endl;
    return 0;
}

Во время компиляции компилятор разворачивает шаблон Factorial для аргумента N = 15 и встречает шаблон для аргумента N = 14 и так далее до N = 0. Поскольку во время этих вычислений переменные не встречаются, они разрешаются во время компиляции, факториал ‹15› в конечном итоге принимает значение 15! то есть 1307674368000 во время компиляции, избегая любых вычислений во время выполнения. Вы можете видеть в собранном коде, что значение 1307674368000 используется непосредственно, чем вызов функции.
ref: Assembly Code (Нет определения функции, так как значение 15! уже вычислено ассемблером и используется в строке 4)

Выполнение обеих реализаций с временным профилированием (и слишком большим количеством итераций) показывает нам влияние оптимизации. Шаблонный код выполняется за ~ 0,03 мс, в то время как рекурсивная версия работает как минимум в 30 раз дольше, как ожидалось, вычисляя факториал и снижая рекурсию во время выполнения.

Другие примеры: шаблоны и специализированные функции.

Более глубокий (более сложный, более полезный) пример взят из Метапрограммы шаблонов (Тодд Велдхуизен). Использование шаблонов для пузырьковой сортировки не кажется интуитивно понятным, поскольку вводится не одно число, а идея состоит в том, чтобы создать шаблон, который разворачивается в specialised function для пузырьковой сортировки, обслуживающей определенное количество элементы. Я настоятельно рекомендую просмотреть пример и проработать детали.

Где это используется?

Первая мысль, которая пришла мне в голову, заключалась в том, почему нам нужно выполнять такую ​​функцию, как вызов шаблона, если не задействованы переменные. Разве это не эквивалентно написанию кода с самим значением (скажем, 1307674368000 вместо Factorial ‹15›). Противоположным аргументом этому является требование абстрагироваться от функций вместо того, чтобы разбрасывать случайные значения по коду.

Следовательно, библиотеки в Boost используют шаблоны для предоставления библиотек для сложных функций, таких как факториалы, синус, косинус и т. Д., Чтобы гарантировать, что значения не вычисляются во время выполнения, где бы они ни были выполнены во время компиляции.

В общем, шаблоны представляют собой хитрый трюк, обеспечивающий вычисление времени компиляции и экономию времени выполнения в критичных по времени проектах (высокочастотная торговля, возможно, воздушные и космические путешествия).

Посетите SO для обсуждения сообществом метапрограммирования шаблонов. Прокомментируйте ниже, если вы использовали это в своем коде и как.