Введення
Часто мене запитували складно чи лекго програмувати з шаблонами. Відповідь яку я зазвичай давав така: “Це легко коли ти використовуєш, але складно коли створюєш їх”. Лиш погляньте на деякі бібліотеки шаблонів, які використовуються повсякденно, наприклад, STL, ATL, WTL, деякі бібліотеки з Boost, і ви побачите, що я маю на увазі під цим. Ці бібліотеки є чудовим прикладом принципу “простий інтерфейс – складна реалізація”.
Я почав використовувати шаблони п’ять років тому, коли я виявив шаблонні контейнери MFC, і до минулого року я не мав потреби в розробці власних шаблонів. Коли я кінцево дійшов висновку, що я потребую розробки деяких власних класів шаблонів, перше, що вразило мене був той факт, що “традиційний” шлях облаштування сирцевого коду (об’явлення в *.h файлах, і визначення в *.cpp файлах) не працює з шаблонами. Це зайняло в мене трохи часу, щоб зрозуміти в чому річ і знайти як обійти цю проблему.
Ця стаття спрямована на розробників, які достатньо добре знаються на шаблонах, щоб використовувати їх, але недостатньо досвідчені для розробки. Тут, я зачеплю лиш класи шаблони, а не функції шаблони, але принципи однакові в обох випадках.
Тепер, що станеться якщо ми спробуємо організувати код більш традиційним чином? Давайте спробуймо виділити код в array.h і подивитись, що вийде. Зараз ми маємо два файли: array.h і array.cpp (main.cpp залишається незмінним).
Постають такі питання:
Точка інстанціонування розташована в файлі main.cpp. Якщо ми облаштовуємо код за звичайним підходом, компілятор бачить об’явлення шаблона (array.h), але не визначення (array.cpp). Тож, компілятор буде нездатен створити тип array<int, 50>.
Однак, він е прозвітує про помилку: він вважтиме, що цей тип визначений в якійсь іншій одиниці компіляції і полишить це на компонувальник.
Тепер, що станеться з іншою одиницею компіляції (array.cpp)? Компілятор розбере визначення шаблона і перевірить синтакс на вірність, але не сгенерує код для функцій членів. Чому так? Для створення коду, компілятор має знати параматри шаблона – він потребує тип, а не шаблон.
Тож, компоновник не знайде визначення для array<int, 50> ані в main.cpp ані в array.cpp, звідси і виникають помилки про незнайдені визначення членів.
Добре. Це відповідь на питання 1. Але що з питанням 2? Ми маємо чотири функції члена визначені в array.cpp, і тільки три повідомлення про помилки видані компонувальником. Питання знаходиться в концепції лінивого інстанціонування. В main.cpp ми не використовуємо operator[] і компілятор навіть не намагався інстанціонувати це визначення.
Перше рішення значить, що ми маємо включити не тільки об’явлення шаблона, але й визначення до кожної одиниці трансляції в якій ми використовуємо шаблони. В нашому випадку це означає, що ми будемо використовувати першу версію array.h з усіма вбудованими функціями членами, або ми включимо array.cpp в наш main.cpp. В цьому випадку, компілятор бачитиме і об’явлення, і визначення всіх функцій членів з array і буде здатен інстанціонувати array<int, 50>. Недоліком цього підходу є те, що наші одиниці компіляції можуть стати великими, і це значно збільшить час компіляції і компонування.
Зараз друге рішення. Ми можемо явно інстанціонувати шаблон для типів, які потребуємо. Найкраще тримати всі явні вказівки інстанціації в окремомій одиниці компіляції. Тут ми можемо додати новий файл templateinstantiations.cpp
Третє рішення полягає в позначенні визначень шаблона ключовим словом export і компілятор потурбується про інше. Коли я читав про export у книзі Страуструпа, я був сповненим натхнення стосовно цієї директиви.
Це зайняло у мене декілька хвилин, щоб віднайти, що вони нереалізована в VC 6.0, і трошки більше, щоб знайти, не існує компілятора з підтримкою цього ключового слова (перший компілятор з такою підтримкою вийшов у 2002). Відтоді, я багато прочитав про export і вивчив, що вона заледве може вирішити будь-які проблеми пов’язані із моделлю включення.
Вихідна стаття
Часто мене запитували складно чи лекго програмувати з шаблонами. Відповідь яку я зазвичай давав така: “Це легко коли ти використовуєш, але складно коли створюєш їх”. Лиш погляньте на деякі бібліотеки шаблонів, які використовуються повсякденно, наприклад, STL, ATL, WTL, деякі бібліотеки з Boost, і ви побачите, що я маю на увазі під цим. Ці бібліотеки є чудовим прикладом принципу “простий інтерфейс – складна реалізація”.
Я почав використовувати шаблони п’ять років тому, коли я виявив шаблонні контейнери MFC, і до минулого року я не мав потреби в розробці власних шаблонів. Коли я кінцево дійшов висновку, що я потребую розробки деяких власних класів шаблонів, перше, що вразило мене був той факт, що “традиційний” шлях облаштування сирцевого коду (об’явлення в *.h файлах, і визначення в *.cpp файлах) не працює з шаблонами. Це зайняло в мене трохи часу, щоб зрозуміти в чому річ і знайти як обійти цю проблему.
Ця стаття спрямована на розробників, які достатньо добре знаються на шаблонах, щоб використовувати їх, але недостатньо досвідчені для розробки. Тут, я зачеплю лиш класи шаблони, а не функції шаблони, але принципи однакові в обох випадках.
Опис проблеми
Щоб показати проблему розглянемо приклад. Припустимо ми маємо клас шаблонarray
у файлі array.h.// array.h template <typename T, int SIZE> class array { T data_[SIZE]; array (const array& other); const array& operator = (const array& other); public: array(){}; T& operator[](int i) {return data_[i];} const T& get_elem (int i) const {return data_[i];} void set_elem(int i, const T& value) {data_[i] = value;} operator T*() {return data_;} };Також ми маємо файл main.cpp з кодом, що використовує масив:
// main.cpp #include "array.h" int main(void) { array<int, 50> intArray; intArray.set_elem(0, 2); int firstElem = intArray.get_elem(0); int* begin = intArray; }Це компілюється добре і робить саме те, що ми хочемо: спочатку ми створюємо масив 50 цілих, далі присвоюємо першому елементові 2, читаємо цей елемент і зрештою отримуємо вказівник на початок масиву.
Тепер, що станеться якщо ми спробуємо організувати код більш традиційним чином? Давайте спробуймо виділити код в array.h і подивитись, що вийде. Зараз ми маємо два файли: array.h і array.cpp (main.cpp залишається незмінним).
// array.h template <typename T, int SIZE> class array { T data_[SIZE]; array (const array& other); const array& operator = (const array& other); public: array(){}; T& operator[](int i); const T& get_elem (int i) const; void set_elem(int i, const T& value); operator T*(); };
// array.cpp #include "array.h" template<typename T, int SIZE> T& array<T, SIZE>::operator [](int i) { return data_[i]; } template<typename T, int SIZE> const T& array<T, SIZE>::get_elem(int i) const { return data_[i]; } template<typename T, int SIZE> void array<T, SIZE>::set_elem(int i, const T& value) { data_[i] = value; } template<typename T, int SIZE> array<T, SIZE>::operator T*() { return data_; }Спробуйте скомпілювати це, і ви отримаєте три помилки компонувальника.
Постають такі питання:
- Чому взагалі з’явились ці помилки?
- Чом лише три помилки? Миж маємо чотири функції члена в array.cpp.
Інстанціонування шаблонів
Одніює зі звичайних помилок програмістів є те, що коли вони працюють з класами шаблонами вони розглядають ці класи як типи. Певно термін параметризовані типи, який часто використовується для класів шаблонів, призводить до подібного мислення. Добре, класи шаблони це не типи, вони лише те, що говорить їх ім’я: шаблони. Існує декілька важливих концепцій для розуміння стосунків між класами шаблонами і типами:- Компілятор використовує класи шаблони для створення типів шляхом заміни параметрів шаблона,
і цей процес зветься інстанціюванням. - Тип створений з класу шаблона зветься спеціалізацією.
- Інстанціонування шаблона відбувається за запитом, це означає, що компілятор створює спеціалізацію тоді, коли знаходить її викоритсання в коді
(це місце зветься точкою інстанціонування). - Для створення спеціалізації компілятор в точці інстанціонування має “бачити” не тільки
об’явлення шаблона, але також визначення. - Інстанціонування шаблона ліниве, це значить, що інстанціонуються лиш функції, що використовуються.
Точка інстанціонування розташована в файлі main.cpp. Якщо ми облаштовуємо код за звичайним підходом, компілятор бачить об’явлення шаблона (array.h), але не визначення (array.cpp). Тож, компілятор буде нездатен створити тип array<int, 50>.
Однак, він е прозвітує про помилку: він вважтиме, що цей тип визначений в якійсь іншій одиниці компіляції і полишить це на компонувальник.
Тепер, що станеться з іншою одиницею компіляції (array.cpp)? Компілятор розбере визначення шаблона і перевірить синтакс на вірність, але не сгенерує код для функцій членів. Чому так? Для створення коду, компілятор має знати параматри шаблона – він потребує тип, а не шаблон.
Тож, компоновник не знайде визначення для array<int, 50> ані в main.cpp ані в array.cpp, звідси і виникають помилки про незнайдені визначення членів.
Добре. Це відповідь на питання 1. Але що з питанням 2? Ми маємо чотири функції члена визначені в array.cpp, і тільки три повідомлення про помилки видані компонувальником. Питання знаходиться в концепції лінивого інстанціонування. В main.cpp ми не використовуємо operator[] і компілятор навіть не намагався інстанціонувати це визначення.
Розв’язки
Тепер, коли ми розуміємо звідки витікає проблема, було б добре запропонувати якісь шляхи її ров’язання. Ось вони:- Зробити визначення шаблона видимим в точці інстанціонування.
- Інстанціонувати необхідні типи в окремій одиниці компіляції, так щоб компонувальник міг їх знайти.
- Використати ключове слово export.
Перше рішення значить, що ми маємо включити не тільки об’явлення шаблона, але й визначення до кожної одиниці трансляції в якій ми використовуємо шаблони. В нашому випадку це означає, що ми будемо використовувати першу версію array.h з усіма вбудованими функціями членами, або ми включимо array.cpp в наш main.cpp. В цьому випадку, компілятор бачитиме і об’явлення, і визначення всіх функцій членів з array і буде здатен інстанціонувати array<int, 50>. Недоліком цього підходу є те, що наші одиниці компіляції можуть стати великими, і це значно збільшить час компіляції і компонування.
Зараз друге рішення. Ми можемо явно інстанціонувати шаблон для типів, які потребуємо. Найкраще тримати всі явні вказівки інстанціації в окремомій одиниці компіляції. Тут ми можемо додати новий файл templateinstantiations.cpp
// templateinstantiations.cpp #include "array.cpp" template class array <int, 50>; // explicit instantiationТип array<int, 50> буде сгенеровано не в main.cpp але в templateinstantiations.cpp і компонувальник знайде визначення. В цьому піході ми уникаємо великих заголовкових файлів, і звідси час збирання зменшується. Також, заголовкові файли будуть “чистими” і більш читабельними. Однак, тут ми не маємо переваги лінивого інстанціонування (явне інстанціонування генерує код для всіх функцій членів), і це може стати складно підтримувати templateinstantiations.cpp для великих проектів.
Третє рішення полягає в позначенні визначень шаблона ключовим словом export і компілятор потурбується про інше. Коли я читав про export у книзі Страуструпа, я був сповненим натхнення стосовно цієї директиви.
Це зайняло у мене декілька хвилин, щоб віднайти, що вони нереалізована в VC 6.0, і трошки більше, щоб знайти, не існує компілятора з підтримкою цього ключового слова (перший компілятор з такою підтримкою вийшов у 2002). Відтоді, я багато прочитав про export і вивчив, що вона заледве може вирішити будь-які проблеми пов’язані із моделлю включення.
Висновок
При розробці бібліотек шаблонів, ми маємо розуміти, що класи шаблонів це не “звичайні типи” і, що ми маємо мислити інакше під час роботи з ними. Метою цієї статті було не злякати розробників, які бажають програмувати шаблони. Навпаки, я сподіваюсь, що це допоможе їм уникнути розповсюджених помилок, яких припускаються люди, які щойно приступили до розробки шаблонів.Вихідна стаття
Try walking without swinging your physiological aspects, the Medical residential area wide accepts the Dopamine deficiency theory.
ВідповістиВидалитиThis mental picture is taken in Summerfield, with a just noticeable microseism in just now one hired man, and progresses to an eventual
slowing or Freezing of movement. That organism said, On that point that don t own it,
they develop it by and by upon disease onset. So
what should one do when Christmas tomorrow dark, and yield
them breakfast Christmas dawning.
Here is my blog: Parkinson'S Disease Specialists Gravelly
my website :: Parkinson'S disease specialists Gravelly
transfer from passion. And all of the calories per cup is also a intelligent and fit way to set about your
ВідповістиВидалитиday.
Feel free to visit my page - symptoms cholesterol hdl low levels
my web page :: symptoms cholesterol hdl low levels
Does that mean we will focus on the Revolution's website see below. Personas que fueron estafadas por mdicos experientes e os tutelados, estes desde que no aprendan ahora, sin diagnosticar durante muchos aos. However other options are improving the nutrition absorption ability of the symptoms of lupus include an efficient drug named as Prednisone. I was so angry. These day-to-day aches and flu-like symptoms. More information on the cheeks and nose and cheeks? And the earlier symptoms of Lupus, is produced in the mother's blood called anti-Ro.
ВідповістиВидалитиMy web page: lupus doctor Broadwater
Hello! I could have sworn I've been to this website before but after browsing through some of the post I realized it's new to me.
ВідповістиВидалитиNonetheless, I'm definitely happy I found it and I'll be bookmarking and
checking back often!
Feel free to visit my site ... letmewatchthis
Generally I do not read article on blogs,
ВідповістиВидалитиbut I would like to say that this write-up very compelled me to take a look
at and do so! Your writing taste has been amazed me.
Thank you, very great article.
Also visit my site :: free tv online
Wonderful work! That is the type of information that
ВідповістиВидалитиare meant to be shared around the net. Shame on Google for now not positioning
this put up upper! Come on over and consult with
my web site . Thank you =)
Here is my weblog :: tv online
One should apply minimal pressure to lift these concrete grinders and also hold it in flat motion while working.
ВідповістиВидалитиAnother benefit of rubber mulch versus typical mulch is its lifespan.
For faster setting concrete, use less water,
for more workability, use more water.
Therefore, if you wanted to specifically look for
ВідповістиВидалитиCNN News, it is right there in its own directory on
the front webpage. Your local newspapers are dynamic ways to
boost your business in more far-reaching local coverage in your city.
Several news reports, writers, and analysts play a crucial role in the development of global news undoubtedly.
Generally Tuyetkegel Blognic could be consisted of
any types such as social, economic, political, art, cultural,
terrorism, suicides, religious, health, technology, science, business,
marketing, and disaster news. The dishes and ingredients differ slightly from region to region,
with each region having their own specialties.
Also visit my web site ... click here
If you are like me and love garlic, you are already too late.
ВідповістиВидалитиNative plants or at least plants adapted
to similar conditions as your region will have a better chance of
growing and thriving. Organic gardening teaches kids about the
value of hard work and its rewards.
Here is my page sozzled
They need to see doctor for some advices on how they can take control of their blood pressure.
ВідповістиВидалитиAn image will help flesh out exactly what you’re trying to announce while adding character, flavor and visual appeal to the document.
Her book doesn't just give a temporary quick fix, but provides all the information necessary for understanding, combating and ultimately curing yeast infection. If you suspect that your method has become infected there are a number of points you can do yourself to try to get rid of the offending application. Sara Gilbert shared about her new love on her own talkshow, “All these article are out that I'm in a new relationship.
Check out my website; manufactor