четвер, 19 лютого 2009 р.

Трошки про Шаблони

Шаблони - це сімейство з однаковою поведінкою без конкретизації типу. При використанні шаблонів цей тип передається у вигляді аргументу або проміжно.
Ось типовий приклад:

template < typename T>
inline const T& max(const T& a, const T& b)
{
return a < b : b ? a;
}

Нетипізовані параметри шаблонів
Нетипізований параметр вважається частиною визначення типу.
Наприклад шаблон стандартного класу bitset < > отримує кількість бітів. Зверніть увагу, що ці бітові поля відносяться до різних типів і їх не можна присвоювати один одному:
bitset < 32> flags32;
bitset < 50> flags50;
Параметри по замовчуванню
Наступний фрагмент дозволяє оголосити об'єкти класу MyClass з одним, або двома параметрами:

template < typename T, typename Container = vector< T> >
class MyClass;

MyClass < int> myClass;
MyClass < int, vector< int> > myClass2;

Ключове слово typename
Слово означає, що наступний за ним ідентифікатор означає тип:

template < class T>
class MyClass {
typename T::SubType* ptr;
...
};

В даному випадку слово typename означає, що SubType є підтипом класу T, відповідно ptr - показчик на тип T::SubType. Без typename ідентифікатор SubType інтерпритується як статичний член класу і рядок сприймається як операція множення значення SubType класу T на значення ptr; Але в даному випадку любий тип який буде підставлений шаблону повинен мати внутрішній тип SubType. SubType може бути як класом оголошеним в середині іншого, так і простом typedef-ом.

Шаблонні функції класів
Такі функції не можуть бути віртуальними і мати параметри по замовчуванню!

class MyClass {
...
template < class T>
void f(T);
};

Шаблон MyClass::f оголошує набір функцій з параметром довільного типу, при умові що цей тип підтримує всі операції, які використовуються в f();
Дана можливість часто використовується для автоматичного приведення типів в класах шаблонів. Наприклад в наступному визначенні аргумент x функції assign() повинен точно відповідати типу об"єкта, для якого він викликається:

template < class T>
class MyClass {
private:
T value;
public:
void assign(const MyClass< T>& x) {
value = x.value; // тип x повинен відповідати типові *this
}
};

Зверніть увагу, використання шаблону з іншим типом при виклику assign() являється помилкою навіть в тому випадку якщо між типами виконується автоматичне приведення:

void f()
{
MyClass < double> b;
MyClass < int> i;
d.assign(b); // OK
d.assign(i); // Помилка: i відноситься до до типу MyClass
// в даному випадку обов"язковим є MyClass
}

Оголошення іншого шаблону у функції дозволяє обійти вимогу співпадіння типів:

template < class T>
class MyClass {
private:
T value;
public:
template < class X>
void assign(const MyClass< X>& x) {
value = x.getValue();
}
T getValue() const {
return value;
}
...
};

тепер функція f() є вірною.
Оскільки тип аргументу x не є типом *this, нам довелося додати функцію getValue() для доступу до приватної змінної.

Шаблонний конструктор використовуються для забезпечення неявних приведень типів при копіюванні об"єктів, але майте на увазі, шаблонний конструктор не заміняє стандартний конструктор копіювання. При точному співпадінні типів буде викликаний стандартний конструктор копіювання:

template < class T>
class MyClass {
public:
// Шаблонний конструктор з автоматичним приведенням тиу
template < class X>
MyClass(const MyClass< X>& x);
};
void f()
{
MyClass < double> xd;
...
MyClass < double> xd2(xd); // Стандартний конструктор копії
MyClass < int> xi(xd); // Шаблонний конструктор
}

Явна ініціалізація базових типів була реалізована для того, щоб ви могли написати код шаблону в якому величині любого типу буде попередньо присвоєне значення по замовчуванню.
Виклик конструктора гарантує, що для всіх базових типів змінна x буде ініціалізована нулем.

template < class T>
void f()
{
T x = T();
...
int i = int(); // ініціалізація змінної i нулем
}

Явна спеціалізація шаблонів.

Хочу звернути увагу на одну особливість використання явної спеціалізації з компілятором майкрософт візуал студіо та компілятором gcc.

#include < iostream>
using namespace std;

class MyClass
{
public:
MyClass() {}
~MyClass() {}
enum State { NoneState = 0x0,
Edited = 0x1,
Inserted = 0x2,
Removed = 0x4,
Saved = 0x8 };
State m_state;
template < State state> void setState() {
cout < < "Usual setState template!!!!" < < endl;
}
///////////// Explicid Specialization for State::Saved
template < > void setState< Saved>() {
cout < < "Explicid Specialication for Saved!!!!" < < endl;
}
};
int main()
{
MyClass m;
m.setState< MyClass::Removed>();
m.setState< MyClass::Saved>();
return 0;
}

Зверніть увагу на рядок

template < > void setState< Saved>() {
cout < < "Explicid Specialication for Saved!!!!" < < endl;
}

Компілятор візуал студіо 2005 без будть яких проблем допускає оголошення явної спеціалізації в середині класу, а от gcc (у мому випадку версії 4.3.0) видає наступну помилку:
error: explicit specialization in non-namespace scope ‘class MyClass’
Тому краще оголосити явну спеціалізацію за межами класу, як це потрібно:

class MyClass
{...
};
///////////// Explicid Specialization for State::Saved
template < > void MyClass::setState< MyClass::Saved>() {
cout < < "Explicid Specialication for Saved!!!!" < < endl;
}

Матеріал взято із книги Nicolai M.Josuttis The C++ Standard Library

Прихильники