http___fmi.wikidot.com_sdp8_1.xml
http___programmingexamples.wikidot.com_templates.xml
1.1 1.2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 3.1 3.2 3.3 3.4 4.1 4.2 4.3 5.1 6.1 6.2 6.3 7.1 8.1 9.1 9.2 9.3 9.4 9.5 10.1 11.1 11.2 11.3 11.4 11.5 12.1 12.2 12.3 13.1 14.1 14.2 14.3 14.4 14.5 14.6 14.7 14.8 14.9 15.1 15.2 15.3 15.4 15.5 15.6
Шаблони на функции. ( от лекциите на Бъчваров ) Възможно е дефинирането на няколко функции с еднакви имена, но различен брой и типове на параметрите. Тези функции се наричат предефинирани(Това не означава "предварително", а "предостатъчно" дефинирани. На английски се наричат overloaded). При извикването на предефинирана функция, компилаторът избира съответния вариант като анализира броя и типа на аргументите при извикването. Предефинираните функции могат да имат различни или еднакви типове на връщаните резултати, но задължително трябва да имат различни списъци от параметри. Две функции различаващи се единствено по типа на връщания резултат водят до грешка. Предефинирани функции обикновено се използват за извършване на близки по обработка дейности над различни типове данни. Компактно решение е използването на шаблонна функция - нова възможност в C++. Описание на шаблонна функция: според типа на аргументите при извикване на тази функция, компилаторът автоматично генерира обектните кодове на функцията, обработващи всеки тип данни. Например един шаблон за сортиране на масив, С++ автоматично генерира отделни шаблонни функции, сортиращи int, float и тн. Шаблоните, подобно на макросите, подпомагат повторно използване на код. Разликата е, че шаблоните помагат да се отстранят много грешки в съответствието на типовете (извършва се пълна проверка на типовете, докато при макросите просто се заменя част от кода с дефиниран предварително низ). Всяко описание на шаблонна функция започва с ключовата дума template след което следва списък от формални параметри на шаблона в ъглови скоби. Всички формални параметри трябва да се предшестват с ключовата дума class или typename. Например: template   template   template Ключовите думи означават всеки вграден тип или име на тип, дефиниран от потребителя. Формални параметри в описанието на шаблон се използват за дефиниране на типовете на параметрите на функцията, типа на връщания резултат и типа на променливите, дефинирани вътре във функцията. Типична грешка е отсъствието на ключовата дума class или typedef пред всеки формален параметър в шаблона на функцията. Например: #include   using std::cout; using std::endl; template   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 може да се предефинира с нешаблонна функция, която извежда елементите на масив от символен тип в подходящо табулиран вид по стълбове. Ако шаблонът се извиква с потребителски тип като параметър и ако този шаблон използва операция ( ==, +, <=, … ) с обекти от този тип, то такива операции трябва да бъдат предефинирани. Ако не са предефинирани, то свързващият редактор ще изведе съобщение за грешка, тъй като компилаторът ще генерира извикване на съответната предефинирана функция на операция, без да обърне внимание, че тези функции не са определени. Компилаторът определя коя функция съответства на даденото извикване по следния начин: отначало се опитва да намери и използва функция, която точно съответства по име и тип на фактическите параметри на извикваната функция Ако не успее, търси шаблон на функция, с помощта на който да генерира шаблонна функция, с точно съответствие на типа на параметрите и името на функцията. Ако такъв шаблон е открит, то компилаторът генерира и използва шаблонната функция. Компилаторът търси шаблон, който напълно съответства на извикваната функция по тип на всеки параметър. Автоматчно преобразуване на типа не се извършва. И като последен опит, компилаторът търси предефинирана функция. Ако съответната функция не е намерена ли са намерени няколко такива, то компилаторът дава съобщение за грешка.
1.1 2.1 2.2 2.3 3.1 4.1 5.1 6.1 7.1 8.1 9.1 10.1 11.1 12.1 12.2 13.1 14.1 14.2 15.1 16.1 17.1 19.1 20.1 21.1 22.1 23.1 24.1 25.1 26.1 28.1 29.1 29.2 30.1 31.1 31.2 32.1 33.1 34.1 35.1 36.1 37.1 38.1 38.2 39.1 40.1 41.1 41.2 42.1 43.1 44.1 45.1 45.2 46.1 47.1 47.2 48.1 49.1 51.1 53.1 56.1 57.1 57.2 58.1 59.1 59.2 60.1 60.2 61.1 62.1 62.2 63.1 64.1 64.2 64.3 65.1 66.1 66.2 66.3 67.1 68.1 68.2 69.1 70.1 71.1 72.1 72.2 73.1 74.1 75.1 75.2 75.3 75.4 76.1 77.1 78.1
Introduction Generic programming involves writing code in a way that is independent of any particular type. Examples of generic programming are library containers and iterator. Templates are the foundation of generic programming. A template is a blueprint or formula for creating a class or a function. C++ templates constitute a compile-time, Turing-complete sublanguage of C++. Template Definitions Defining a Function Template A function template is a type-independent function that is used as a formula for generating a type-specific version of the function. Example 1 Function to compare two values and indicate whether the first is less than, equal to, or greater than the second. First attempt might be to define several overloaded functions: // returns 0 if the values are equal, -1 if v1 is smaller, 1 if v2 is smaller int compare(const string &v1, const string &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } int compare(const double &v1, const double &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } Having to repeat the body of the function for each type that we compare is tedious and error-prone. More importantly, we need to know in advance all the types that we might ever want to compare. Example 2 // implement strcmp-like generic compare function // returns 0 if the values are equal, 1 if v1 is larger, -1 if v1 is smaller // one type parameter, T. // Which actual type T represents is determined by the compiler based on how the function is used. template   int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } Template parameters represent types or values we can use in the definition of a class or function. Example 3 int main () // T is int; // compiler instantiates int compare(const int&, const int&) cout << compare(1, 0) << endl; // T is string; // compiler instantiates int compare(const string&, const string&) string s1 = "hi", s2 = "world"; cout << compare(s1, s2) << endl; return 0; Defining a Function Template Rather than defining a new function for each type, we can define a single function template. A function template is a type-independent function that is used as a formula for generating a type-specific version of the function. A template definition starts with the keyword template followed by a template parameter list, which is a comma-separated list of one or more template parameters bracketed by the less-than ( ) tokens. When we use a function template, the compiler infers what template argument(s) to bind to the template parameter(s). Once the compiler determines the actual template argument(s), it instantiates an instance of the function template for us. Inline Function Templates A function template can be declared inline function |inline in the same way as a nontemplate function. Example 4 // ok: inline specifier follows template parameter list template   inline T min(const T&, const T&); // error: incorrect placement of inline specifier inline template   T min(const T&, const T&);}} Defining a Class Template Example Implementation of a Queue class. Our Queue template takes a single template type parameter named Type. template   class Queue { public: Queue (); // default constructor Type &front (); // return element from head of Queue const Type &front () const; void push (const Type &); // add element to back of Queue void pop(); // remove element from head of Queue bool empty() const; // true if no elements in the Queue private: // ... }; Template Type Parameters Type parameters consist of the keyword class or the keyword typename followed by an identifier. In a template parameter list, these keywords have the same meaning: They indicate that the name that follows represents a type. The keyword typename was added to C++ as part of Standard C++, so older programs are more likely to use the keyword class exclusively. Nontype Template Parameters A template parameter need not be a type. Nontype parameters are replaced by values when the function is called. The type of that value is specified in the template parameter list. Example The following function template declares array_init as a function template with one type and one nontype template parameter. The function itself takes a single parameter, which is a reference to an array. // initialize elements of an array to zero template   void array_init(T (&parm)[N]) for (size_t i = 0; i != N; ++i) parm[i] = 0; Writing Generic Programs The operations performed inside a function template constrains the types that can be used to instantiate the function. It is up to the programmer to guarantee that the types used as the function arguments actually support any operations that are used, and that those operations behave correctly in the context in which the template uses them. Instantiation The compiler uses the template to generate type-specific versions of the specified class or function. The process of generating a type-specific instance of a template is known as instantiation. A template is instantiated when we use it. A class template is instantiated when we refer to the an actual template class type, and a function template is instantiated when we call it or use it to initialize or assign to a pointer to function. Example Queue  qi; // the compiler automatically creates a class named Queue  Queue  qs; // each occurrence of Type would be replaced by string. Queue qs; // error: which template instantiation? Template Argument Deduction To determine which functions to instantiate, the compiler looks at each argument. If the corresponding parameter was declared with a type that is a type parameter, then the compiler infers the type of the parameter from the type of the argument. The process of determining the types and values of the template arguments from the type of the function arguments is called template argument deduction. Function-Template Explicit Arguments In some situations, it is not possible to deduce the types of the template arguments. This problem arises most often when a function return type must be a type that differs from any used in the parameter list. In such situations, it is necessary to override the template argument deduction mechanism and explicitly specify the types or values to be used for the template parameters. Template Compilation Models When the compiler sees a template definition, it does not generate code immediately. The compiler produces type-specific instances of the template only when it sees a use of the template, such as when a function template is called or an object of a class template is defined. Class Template Members Queue Implementation Strategy Two classes: - Class QueueItem will represent a node in Queue's linked list. This class has two data members: item and next - Class Queue will provide the interface functions The QueueItem Class Class QueueItem is a private class—it has no public interface. We intend this class to be used to implement Queue and have not built it for general use. Hence, it has no public members. We'll need to make class Queue a [[friends|friend]]] of QueueItem so that its members can access the members of QueueItem. template   class QueueItem { // private class: no public section QueueItem(const Type &t): item(t), next(0) { } Type item; // value stored in this element QueueItem *next; // pointer to next element in the Queue }; The Queue Class template   class Queue { public: // empty Queue Queue(): head(0), tail(0) { } // copy control to manage pointers to QueueItems in the Queue Queue(const Queue &Q): head(0), tail(0) { copy_elems(Q); } Queue& operator=(const Queue&); ~Queue() { destroy(); } // return element from head of Queue // unchecked operation: front on an empty Queue is undefined Type& front() { return head->item; } const Type &front() const { return head->item; } void push(const Type &); // add element to back of Queue void pop (); // remove element from head of Queue bool empty () const { // true if no elements in the Queue return head == 0; } private: QueueItem  *head; // pointer to first element in Queue QueueItem  *tail; // pointer to last element in Queue // utility functions used by copy constructor, assignment, and destructor void destroy(); // delete all the elements void copy_elems(const Queue&); // copy elements from parameter };