Шаблони в С ++ є засобами метапрограмування і реалізують поліморфізм часу компіляції. Що це таке?
Це коли ми пишемо код з поліморфним поведінкою, але сама поведінка визначається на етапі компіляції - тобто на противагу поліморфізму віртуальних функцій, отриманий бінарний код вже буде мати постійне поведінку.
Ми використовуємо шаблони для краси. Кожен С ++ розробник знає, що таке краса, краса - це коли код компактний. зрозумілий і швидкий.
Мета-магія і неявні інтерфейси
Перші заклинання: чарівна дубина
Конкретизуємо наш шаблон і подивимося, які типи ми отримали для різних параметрів шаблону:
У висновку програми видно, що типи конкретизацій шаблону різні навіть для еквівалентних типів - unsigned char # 038; char. При цьому вони ідентичні для char # 038; CHAR, тому що typedef не створює тип, а лише дає йому інше ім'я. Ідентичні вони і для виразів 1 і 2-1, тому що компілятор обчислює вирази і замість 2-1 використовує 1.
Звідси і випливає, що ми не можемо використовувати для шаблонів роздільну компіляцію без додаткових проблем:
Взагалі, в стандарті С ++ для цього є ключове слово export. однак ця фіча занадто важко буде реалізувати і відсутній в більшості компіляторів. Є компілятори, які її підтримують, але не раджу її використовувати в переносимому коді.
Крім класів існують і шаблони функцій:
Якщо компілятор може вивести тип параметра шаблона з типу параметрів - він так і вчинить, при цьому нам не потрібно вказувати його в коді. Якщо немає, то ми можемо визначити роздільну функцію:
Вона не несе ніяких накладних витрат.
Спеціалізація - це новий рівень
Зазвичай використовуючи шаблони ми хочемо написати універсальний код, проте в деяких випадках ми можемо програти в продуктивності. Для вирішення проблеми існує спеціальне закляття - спеціалізація Шалона. Спеціалізація - це повторне визначення шаблону з конкретним типом або класом типів.
Компілятор сам вибере найбільш точно відповідну спеціалізацію, в прикладі це клас типів "покажчик на тип".
Зловісна магія: рекурсія
Спеціалізації і той факт, що ми можемо використовувати шаблони в шаблонах, дає дам одну дуже цікаву можливість - рекурсія часу компіляції.
Найпростіший і популярний приклад - обчислення будь-якого ряду або полінома, скажімо, сума арифметичної прогресії:
Дивимося ... Працює! Круто? Збільшимо кількість ітерацій до 500:
Тепер компіляція займає більше часу, при цьому час виконання програми - константа! Чудеса!
Не роби козу якщо хотів грозу
Тут є пара моментів.
Максимальна глибина рекурсії за замовчуванням обмежена реалізацією, для нового gcc це 900, для старих версій він менше. параметр
знімає це обмеження.
Другий підводний камінь - не чекайте звітів про помилки. Міняємо прогресію на факторіал:
Отримуємо некоректний результат, і жодного попередження ...
Третій момент, очевидний: ми можемо створити занадто багато майже однакових конкретизацій шаблону і замість приросту продуктивності отримати приріст бінарного коду.
Потужні заклинання древніх
А чи можна поєднати магію успадкування з шаблонної магією?
Стародавні використовують для цього заклинання CRTP. Ідея проста: можна застосувати не віртуальне успадкування і забезпечити поліморфну поведінку за допомогою явного приведення типу спадкоємця до типу батька. Давайте розглянемо приклад використання.
Ми отримуємо успадковані inline методи з поліморфним поведінкою! Хто скаже що це не круто - мій ворог назавжди.
Стародавні також радять додавати в конструктор батька щось типу того:
щоб демони, розбуджені потужним заклинанням, не змогли завдати шкоди викликав їх магу.
Є ще багато таємних технік, древніх і не дуже. Сподіваюся на не скоро зустріч / * в пеклі * /, і нехай прибуде з вами сила древніх.