Шаг 33.
Библиотека STL.
Шаблонные функции классов

    На этом шаге мы рассмотрим особенности создания и использования шаблонных функций классов.

    Допускается определение функций классов в виде шаблонов. С другой стороны, такие функции не могут быть виртуальными и не имеют параметров по умолчанию. Пример:

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

    Шаблон MyClass:f объявляет набор функций с параметром произвольного типа. При вызове функции может передаваться любой аргумент (при условии, что его тип поддерживает все операции, используемые в f).

    Данная возможность часто используется для автоматического преобразования типов в классах шаблонов. Например, в следующем определении аргумент х функции assign() должен точно соответствовать типу объекта, для которого он вызывается:

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

    Однако использование шаблона с другим типом при вызове assign() является ошибкой даже в том случае, если между типами выполняется автоматическое преобразование:

   void f()
   {
     MyClass<double> d; 
     MyClass<int> i:
     d.assign(d);  // OK
     d.assign(i):  // ОШИБКА: i относится к типу MyClass<int>.
                       // но в данном случае ОБЯЗАТЕЛЕН тип MyClass<double>
   }

    Определение другого шаблона для функции позволяет обойти требование точного совпадения типов. В этом случае аргумент может иметь произвольный тнп шаблона (при условии, что типы совместимы по присваиванию):

   template <class T> class MyClass 
   { 
     private:
       Т value; 
     public:
       template <class X> // Шаблон функции позволяет
       void assign (const MyClass<X>& x) {    // использовать другие типы
          value = x.getValue();	// шаблонов при присваивании.
       }
       Т getValue () const { return value; }
     .    .    .
   };

   void f() 
   {
      MyClass<double> d;
      MyClass<int> i:
      d.assign(d):  // OK
      d.assign(i);  // OK (тип int совместим с double no присваиванию) 
   }

    Поскольку тип аргумента х функции assign() теперь отличен от *this, прямой доступ к закрытым и защищенным членам MyClass() невозможен. Вместо этого приходится использовать обходные пути вроде функции getValue() из рассмотренного примера.

    Шаблонный конструктор представляет собой особую разновидность шаблона, определяемую внутри класса. Шаблонные конструкторы обычно определяются для обеспечения неявных преобразований типов при копировании объектов. Учтите, что шаблонный конструктор не замещает стандартный копирующий конструктор. При точном соответствии типов будет сгенерирован и вызван стандартный копирующий конструктор. Пример:

   template <class T> class MyClass 
   { public: // Шаблонный конструктор с автоматическим преобразованием типа
                // не замещает стандартный копирующий конструктор
        template <class U>
        МуСlass (const MyClass<U>& x);
        .    .    .
   }; 
   
  void f()
  {
     MyClass<double> xd;
     .    .    .
     MyClass<double> xd2(xd): // Вызывает стандартный копирующий конструктор 
     MyClass<int> xi(xd);   // Вызывает шаблонный конструктор
     .    .    .
   }

    В приведенном примере тип xd2 соответствует типу xd, поэтому объект инициализируется копирующим конструктором по умолчанию. С другой стороны, тип xi отличается от типа xd, поэтому объект инициализируется шаблонным конструктором. Следовательно, если вы определяете шаблонный конструктор, не забудьте также определить собственный копирующий конструктор, если стандартный копирующий конструктор вас не устраивает.

    На следующем шаге мы рассмотрим шаблоны вложенных классов и явную инициализацию базовых типов.




Предыдущий шаг Содержание Следующий шаг