Шаблони на функции

8. Шаблони на функции. ( от лекциите на Бъчваров )

Възможно е дефинирането на няколко функции с еднакви имена, но различен брой и типове на параметрите. Тези функции се наричат предефинирани(Това не означава "предварително", а "предостатъчно" дефинирани. На английски се наричат overloaded). При извикването на предефинирана функция, компилаторът избира съответния вариант като анализира броя и типа на аргументите при извикването. Предефинираните функции могат да имат различни или еднакви типове на връщаните резултати, но задължително трябва да имат различни списъци от параметри. Две функции различаващи се единствено по типа на връщания резултат водят до грешка. Предефинирани функции обикновено се използват за извършване на близки по обработка дейности над различни типове данни. Компактно решение е използването на шаблонна функция - нова възможност в C++.

Описание на шаблонна функция: според типа на аргументите при извикване на тази функция, компилаторът автоматично генерира обектните кодове на функцията, обработващи всеки тип данни. Например един шаблон за сортиране на масив, С++ автоматично генерира отделни шаблонни функции, сортиращи int, float и тн. Шаблоните, подобно на макросите, подпомагат повторно използване на код. Разликата е, че шаблоните помагат да се отстранят много грешки в съответствието на типовете (извършва се пълна проверка на типовете, докато при макросите просто се заменя част от кода с дефиниран предварително низ).

Всяко описание на шаблонна функция започва с ключовата дума template след което следва списък от формални параметри на шаблона в ъглови скоби. Всички формални параметри трябва да се предшестват с ключовата дума class или typename. Например:

template < class T >
template < typename ElementType >
template < class BorderType, class FillType >

Ключовите думи означават всеки вграден тип или име на тип, дефиниран от потребителя. Формални параметри в описанието на шаблон се използват за дефиниране на типовете на параметрите на функцията, типа на връщания резултат и типа на променливите, дефинирани вътре във функцията. Типична грешка е отсъствието на ключовата дума class или typedef пред всеки формален параметър в шаблона на функцията.

Например:

#include <iostream>
using std::cout;
using std::endl;
template <class T>
void printArray (const T* array, const int count)
{
    for (int i = 0; i < count; i++)
    cout << array[i] << " ";
    cout << endl;
 }

В шаблонната функция printArray е дефиниран един формален параметър Т. Вместо идентификатора Т може да бъде използван всеки друг допустим идентификатор. За типа на масива, който ще се извежда в printArray, Т се нарича параметър на типа. Когато компилирането открие в програмата извикване на функцията printArray, той заменя Т в цялата област на дефиниране на шаблона на функцията с типа на първия фактически параметър от обръщението към функцията printArray и С++ създава шаблон на функция за извеждане на масив от указания тип. След това създадената функция се компилира.

void main
{
    const int aCount = 5, bCount = 7, cCount = 6;
    int a[ aCount ] = { 1,2,3,4,5 };
    double b[bCount] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
    char c[cCount] = "Hello";
    cout << " Array a " << endl;
    printArray( a, aCount );
    cout << " Array b " << endl;
    printArray( b, bCount );
    cout << " Array c " << endl;
    printArray( c, cCount );
}

в Главната функция има извикване на 3 шаблонни функции printArray. Едната извежда масив от тип int, другата - double и третата - char. В резултат на извикването на printArray компилаторът създава шаблонна функция printArray, в която вместо параметър Т се използва тип int (при извикването printArray(a, aCount)). Извикването на функцията printArray(b, bCount) води до създаването на втора шаблонна функция, в която параметър за тип Т се заменя с double. При обработката на извикването на printArray(c, cCount), компилаторът създава трета шаблонна функция, в която параметър за тип е char.
При използването на шаблонна функция трябва да се отчита, че програмата може да създава твърде много копия на шаблонни функции и класове. За тези копия може да са нужни големи ресурси памет. В разглеждания пример шаблоните позволяват да се избегне необходимостта от написването на 3 отдени предефинирани функции с прототипи:

void printArray( const int*, const int );
void printArray( const double*, const int );
void printArray( const char*, const int );

Шаблонните функции и предефинираните са тясно свързани. Всички шаблонни функции имат едно и също име, заради това компилаторът използва механизма за предефиниране, за да осигури извикването на съответната функция. Самият шаблон на функция може да се предефинира по няколко начина. Може да се дефинират други шаблони, имащи същото име, но с различни набори от параметри. Например шаблонът на printArray може да се предефинира с друг шаблон printArray, който има допълнителни параметри, lowSubscript и highSubscript, за определяне на тази част от масива, която ще се изведе. Шаблонна функция може да бъде предефинирана и ако се въведе друга нешаблонна функция със същото име, но с друг набор от параметри от функцията. Например шаблонната функция printArray може да се предефинира с нешаблонна функция, която извежда елементите на масив от символен тип в подходящо табулиран вид по стълбове. Ако шаблонът се извиква с потребителски тип като параметър и ако този шаблон използва операция ( ==, +, <=, … ) с обекти от този тип, то такива операции трябва да бъдат предефинирани. Ако не са предефинирани, то свързващият редактор ще изведе съобщение за грешка, тъй като компилаторът ще генерира извикване на съответната предефинирана функция на операция, без да обърне внимание, че тези функции не са определени.

Компилаторът определя коя функция съответства на даденото извикване по следния начин: отначало се опитва да намери и използва функция, която точно съответства по име и тип на фактическите параметри на извикваната функция Ако не успее, търси шаблон на функция, с помощта на който да генерира шаблонна функция, с точно съответствие на типа на параметрите и името на функцията. Ако такъв шаблон е открит, то компилаторът генерира и използва шаблонната функция. Компилаторът търси шаблон, който напълно съответства на извикваната функция по тип на всеки параметър. Автоматчно преобразуване на типа не се извършва. И като последен опит, компилаторът търси предефинирана функция. Ако съответната функция не е намерена ли са намерени няколко такива, то компилаторът дава съобщение за грешка.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License