Введення в магію шаблонів, savepearlharbor

Шаблони в С ++ є засобами метапрограмування і реалізують поліморфізм часу компіляції. Що це таке?
Це коли ми пишемо код з поліморфним поведінкою, але сама поведінка визначається на етапі компіляції - тобто на противагу поліморфізму віртуальних функцій, отриманий бінарний код вже буде мати постійне поведінку.


Ми використовуємо шаблони для краси. Кожен С ++ розробник знає, що таке краса, краса - це коли код компактний. зрозумілий і швидкий.

Мета-магія і неявні інтерфейси

Перші заклинання: чарівна дубина


Конкретизуємо наш шаблон і подивимося, які типи ми отримали для різних параметрів шаблону:

У висновку програми видно, що типи конкретизацій шаблону різні навіть для еквівалентних типів - unsigned char # 038; char. При цьому вони ідентичні для char # 038; CHAR, тому що typedef не створює тип, а лише дає йому інше ім'я. Ідентичні вони і для виразів 1 і 2-1, тому що компілятор обчислює вирази і замість 2-1 використовує 1.
Звідси і випливає, що ми не можемо використовувати для шаблонів роздільну компіляцію без додаткових проблем:

Взагалі, в стандарті С ++ для цього є ключове слово export. однак ця фіча занадто важко буде реалізувати і відсутній в більшості компіляторів. Є компілятори, які її підтримують, але не раджу її використовувати в переносимому коді.
Крім класів існують і шаблони функцій:

Якщо компілятор може вивести тип параметра шаблона з типу параметрів - він так і вчинить, при цьому нам не потрібно вказувати його в коді. Якщо немає, то ми можемо визначити роздільну функцію:

Вона не несе ніяких накладних витрат.

Спеціалізація - це новий рівень


Зазвичай використовуючи шаблони ми хочемо написати універсальний код, проте в деяких випадках ми можемо програти в продуктивності. Для вирішення проблеми існує спеціальне закляття - спеціалізація Шалона. Спеціалізація - це повторне визначення шаблону з конкретним типом або класом типів.

Компілятор сам вибере найбільш точно відповідну спеціалізацію, в прикладі це клас типів "покажчик на тип".

Зловісна магія: рекурсія

Спеціалізації і той факт, що ми можемо використовувати шаблони в шаблонах, дає дам одну дуже цікаву можливість - рекурсія часу компіляції.

Найпростіший і популярний приклад - обчислення будь-якого ряду або полінома, скажімо, сума арифметичної прогресії:

Дивимося ... Працює! Круто? Збільшимо кількість ітерацій до 500:

Тепер компіляція займає більше часу, при цьому час виконання програми - константа! Чудеса!

Не роби козу якщо хотів грозу

Тут є пара моментів.

Максимальна глибина рекурсії за замовчуванням обмежена реалізацією, для нового gcc це 900, для старих версій він менше. параметр

знімає це обмеження.
Другий підводний камінь - не чекайте звітів про помилки. Міняємо прогресію на факторіал:

Отримуємо некоректний результат, і жодного попередження ...
Третій момент, очевидний: ми можемо створити занадто багато майже однакових конкретизацій шаблону і замість приросту продуктивності отримати приріст бінарного коду.

Потужні заклинання древніх

А чи можна поєднати магію успадкування з шаблонної магією?

Стародавні використовують для цього заклинання CRTP. Ідея проста: можна застосувати не віртуальне успадкування і забезпечити поліморфну ​​поведінку за допомогою явного приведення типу спадкоємця до типу батька. Давайте розглянемо приклад використання.

Ми отримуємо успадковані inline методи з поліморфним поведінкою! Хто скаже що це не круто - мій ворог назавжди.
Стародавні також радять додавати в конструктор батька щось типу того:

щоб демони, розбуджені потужним заклинанням, не змогли завдати шкоди викликав їх магу.

Є ще багато таємних технік, древніх і не дуже. Сподіваюся на не скоро зустріч / * в пеклі * /, і нехай прибуде з вами сила древніх.

Схожі статті