Skip to content

CPP 14. Шаблоны типов. Шаблоны классов. Полная или частичная специализация шаблонов классов. Параметры шаблонов задаваемых по умолчанию. Шаблоны с переменным числом параметров. Пространства имен.

Kozlitin Maxim edited this page Jun 15, 2023 · 1 revision

Шаблон

Если есть функции, которые выполняют одни и те же действия, но для разных типов данных, то их можно шаблонизировать.

В С надо либо плодить однотипные функции, что плохо по определению, либо использовать макросы с параметрами, что опасно, так как подстановка идет на этапе компиляции, а проверка - на этапе выполнения.

В С++ эта проблема решается шаблонами.

Шаблоны решают проблему дублирования кода

Шаблон может быть для функции, класса, метода класса, своего типа. Во время компиляции будет подстановка параметров шаблона с проверкой шаблона.

Шаблон не является ни классом, ни функцией. Функция и класс генерируется на основе шаблона во время использования.

Компилятор встречает вызов шаблона функции (создание класса) - смотрит, может ли он быть использован и если может, то по шаблону создается функция(класс).

Параметры шаблона

Параметрами шаблона могут быть типы и параметры значений.

Типы - простые типы. Производные типы. Классы.

Параметры значения - только константные параметры целого типа или указатели с внешним связыванием. (много ниже)

  • Синтаксис шаблона:

    template<[Параметры шаблона]>
    /////функция или класс или тип\\\\\

Параметры создаются со словами typename или class. Они эквивалентны.

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

Возможны два варианта создания функции по шаблону.

Если функция принимает параметры шаблона, то компилятор может сгенерировать функцию по её вызову(в зависимости от того, какого типа параметры передаются). Так же можно явно указать с какими значениями параметров сгенерировать функцию (явно указать в скобочках).

  • Пример вариантов

    template <typename Type>
    Type sum(Type first, Type second);
     
    //Первый пример:
    double first = 1.0;
    double second = 2.0;
     
    double result = Sum(first, second); // по типам параметров
     
    //Второй:
    int result = Sum<int>(1, 2);  // явно в угловых скобках

Шаблонные функции можно перегружать.

Шаблонный тип:

В языке С использовался typedef.

В С++ - using

  • Синтаксис:

    typedef void (*func)(int); // C
    using func = void (*)(int); // C++

using ИмяТипа = АбстрактныйОписатель (определение переменной без её имени)

Для using можно определять шаблон:

  • Код шаблонного using

    template <typename Type> 
    using func = int(*)(Type, Type)

Специализация

Любую функцию можно перегружать или делать специализацию функции

Специализация - определение функции с конкретными значениями параметров

Полная - для всех параметров

Неполная - не для всех

Специализация тоже является шаблоном

  • Специализация синтаксис:

    // Шаблон функции
    template <typename Type>
    void swap(Type& val1, Type& val2) {}
    
    // Полная специализация 
    template<> // Показываем, что функция - шаблон
    void swap<float>(float& val1, float& val2) {}

Приоритет для компилятора (в порядке уменьшения):

  1. Перегруженная функция
  2. Специализации
  3. Шаблон
  • Пример 11. Правило вызова функций.

    template <typename Type>
    void swap(Type& val1, Type& val2)
    {
      Type temp = val1; val1 = val2; val2 = temp;
    }
     
    template<>
    void swap<float>(float& val1, float& val2)
    {
      float temp = val1; val1 = val2; val2 = temp;
    }
     
    void swap(float& val1, float& val2)
    {
      float temp = val1; val1 = val2; val2 = temp;
    }
     
    void swap(int& val1, int& val2)
    {
      int temp = val1; val1 = val2; val2 = temp;
    }
     
    void main()
    {
      const int N = 2;
      int a1[N];
      float a2[N];
      double a3[N];
     
      swap(a1[0], a1[1]);     // swap(int&, int&)
      swap<int>(a1[0], a1[1]);   // swap<int>(int&, int&)
      swap(a2[0], a2[1]);     // swap(float&, float&)
      swap<float>(a2[0], a2[1]); // swap<>(float&, float&)
      swap(a3[0], a3[1]);     // swap<double>(double&, double&)
    }

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

Ставится auto и decltype

  • Пример 12. Определение типа возвращаемого значения для шаблона функции.

    template <typename T, typename U>
    auto sum(const T& elem1, const U& elem2) -> decltype(elem1 + elem2)// decltype можно не писать вроде
    {
      return elem1 + elem2;
    }
     
    int main()
    {
      auto s = sum(1, 1.2); // будет тип double, так как int + double = double
     
      cout << "Result: " << s << endl;
    }

Классы

Методы шаблонного класса - также шаблоны. В обычном классе могут быть шаблонные методы

  • тык

    template <Type>
    Class A:
    {
    Type var;
    void f();
    }
     
    template <Type>
    void A<Type>::f()
    {
    }
  • Параметры значения:

    template <typename Type, const char* string>
    Class A:
    {}
     
     
    const char* str = "asd";
    A<str> object;  // ошибка. нет внешнего связывания. константная строка может инициализироваться неконстантной
     
    extern char S[] = "asd";
    A<S> obj2; // ok, так как есть внешнее связывание
     
    template <int b>
    Class A:
    {}

Под каждый параметр создастся отдельный класс, надо быть аккуратней, они не взаимозаменяемы.

Полная специализация

  • Код

    template <typename T>
    class A {...};
    
    template <>
    class A<float> {...};

Частичная

  • Код

    // Второй параметр - по умолчанию.
    // То есть если компилятор генерит класс по шаблону, а в параметры передаётся
    // только один параметр, второй будет приниматься за double.
    template <typename T1, typename T2 = double> 
    class A {};
    
    // Частичная специализация, когда два параметра равны
    template <typename T>
    class A<T, T> {};
    
    // Частичная специализация, второй параметр - int
    template <typename T>
    class A<T, int> {};

Параметры шаблона задаваемые по умолчанию

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

  • Код

    template <typename T1, typename T2 = double> 
    class A
    {
    public:
    	A() { cout << "constructor of template A<T1, T2>;" << endl; }
    };

Шаблоны с переменным числом параметров.

Может быть заменен initializer_list. Нужно ограничивать, как и рекурсию (в примере - функция терминатор с одним параметром) Ну тут добавить нечего, вот пример, вот такой вот синтаксис.

  • Пример 5. Шаблон функции с переменным числом параметров.

    template <typename Type>
    Type sum(Type value)
    {
      return value;
    }
     
    template <typename Type, typename ...Args> // сначала три точки при объявлении template
    Type sum(Type value, Args... args)         // три точки после типа при использовании
    {
      return value + sum(args...);
    }
     
    int main()
    {
      cout << sum(1, 2, 3, 4, 5) << endl;
     
      return 0;
    }

Пространства имен

  • Пример кода

    namespace myNamespace
    {
       class A {...};
    }
     
    // Доступ извне в пространство имен происходит через доступ к контексту
    myNamespace::A obj();
     
    //Или включить всё пространство:
    using namespace myNamespace;
    A obj();                                                                                                                                                                                                                                                                                                                                                                                 

Имя пространства имен может быть опущено (анонимное пространство имен). Тогда к нему нельзя получить доступ извне

Можно углубляться, то есть ns1::ns2::ns3

Надо стараться избегать злоупотребления.

Clone this wiki locally