MyTetra Share
Делитесь знаниями!
Что обозначает в C++ шаблон без параметров "template <>"
Время создания: 06.02.2024 10:24
Текстовые метки: c++, си++, шаблон, template, без параметров, пустой, специализация, полная, частичная
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/17072042711x5gdfen5m/text.html на raw.github.com

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

Специализация шаблона может быть полной и частичной.


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

При полной специализации шаблона указываются значения для всех параметров шаблона. И тогда для указанного набора аргументов (типов) компилятор будет использовать специализацию шаблона, а не создавать класс на основе шаблона. Например:



#include <iostream>


// Обычный шаблон класса

template <typename T>

class Person {

public:

    Person(std::string name) : name{name}

    { }

    void setId(T value) { id = value;}

    void print() const

    {

        std::cout << "Id: " << id << "\tName: " << name << std::endl;

    }

private:

    T id;

    std::string name;

};

// Полная специализация шаблона для типа unsigned

template <>

class Person<unsigned> {

public:

    Person(std::string name) : name{name}

    {

        id = ++count;

    }

    void print() const

    {

        std::cout << "Id: " << id << "\tName: " << name << std::endl;

    }

private:

    static inline unsigned count{};

    unsigned id;

    std::string name;

};

int main()

{

    // объекты создаются на основе полной специализации шаблона

    Person<unsigned> tom{"Tom"};

    tom.print();    // Id: 1    Name: Tom

    Person<unsigned> sam{"Sam"};

    sam.print();    // Id: 2    Name: Sam

    // объект создается на основе класса, генерируемого компилятором по шаблону

    Person<std::string> bob{"Bob"};    // T - std::string

    bob.setId("id1345");

    bob.print();    // Id: id1345  Name: Bob

}


Вначале надо определить сам шаблон. В данном случае это шаблон класса Person, который принимает один параметр. Этот параметр внутри шаблона используется для определения типа переменной id.

После шаблона класса идет специализация шаблона:



// Полная специализация шаблона для типа unsigned

template <>

class Person<unsigned> {

    public:

    Person(std::string name) : name{name}

    {

        id = ++count;

    }

    void print() const

    {

        std::cout << "Id: " << id << "\tName: " << name << std::endl;

    }

private:

    static inline unsigned count{};

    unsigned id;

    std::string name;

};



В данном случае специализация полная, так как для всех параметров шаблона (по сути для единственного параметра шаблона) указано значение - в данном случае тип unsigned. В этом случае после ключевого слова template идут пустые угловые скобки <>. То есть данная специализация будет применяться только для тех случаев, когда параметр шаблона представляет тип unsigned.

Cпециализация шаблона класса необязательно должна иметь те же члены, что и сам шаблон: специализация шаблона может изменять, добавлять или опускать члены без ограничений. Так, в данном случае id представляет тип unsigned и генерируется в конструкторе на основе дополнительно добавленной статической переменной. Эта статическая переменная увеличивается с каждым новым созданным объектом, поэтому каждый новый объект Person<unsigned> для id будет получать значение на 1 больше, чем предыдущего. При этом функции setId в специализации нет, он нам не нужен.

В функции main() мы можем использовать эту специализацию для создания объектов Person:



Person<unsigned> tom{"Tom"};

tom.print();    // Id: 1    Name: Tom


Person<unsigned> sam{"Sam"};

sam.print();    // Id: 2    Name: Sam



Поскольку для этих объектов в качестве параметра шаблона указан тип unsigned, то будет использоваться наша специализация шаблона.


Для всех других параметров шаблона компилятор будет сам создавать определение класса. Например:



Person<std::string> bob{"Bob"}; // T - std::string


bob.setId("id1345");

bob.print(); // Id: id1345, Name: Bob



Здесь параметру шаблона передается в качестве значения тип std::string. Соответственно переменная id будет представлять строку, а для ее установки применяется функция setId, в которую передается строка.


Частичная специализация шаблона

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



#include <iostream>

   

// Обычный шаблон класса

template <typename T, typename K>

class Person {

public:

    Person(std::string name, K phone) : name{name}, phone{phone}

    { }

    void setId(T value) { id = value;}

    void print() const

    {

        std::cout << "Id: " << id << "\tName: " << name << "\tPhone: " << phone << std::endl;

    }

private:

    T id;

    std::string name;

    K phone;

};

 

// Частичная специализация шаблона для типа unsigned

template <typename K>

class Person<unsigned, K> {

public:

    Person(std::string name, K phone) : name{name}, phone{phone}

    {

        id = ++count;

    }

    void print() const

    {

        std::cout << "Id: " << id << "\tName: " << name << "\tPhone: " << phone << std::endl;

    }

private:

    static inline unsigned count{};

    unsigned id;

    std::string name;

    K phone;

};

 

int main()

{

    Person<std::string, std::string> bob{"Bob", "+1234567688"};    // T - std::string

    bob.setId("13");

    bob.print();    // Id: 13  Name: Bob       Phone: +1234567688

 

    Person<unsigned, std::string> tom{"Tom", "+4444444444"};

    tom.print();    // Id: 1   Name: Tom       Phone: +4444444444

 

    Person<unsigned, std::string> sam{"Sam", "+555555555"};

    sam.print();    // Id: 2   Name: Sam       Phone: +555555555

}



Здесь для примера шаблон имеет два параметра T и K:



// Частичная специализация шаблона для типа unsigned

template <typename T, typename K>

class Person {



Параметр T устанавливает тип для переменной id, а параметр K - для номера телефона, который хранится в переменной phone (мы можем передать номер телефона в виде строки или числа - последовательности цифр).


После определения шаблона идет частичная специализация шаблона для типа unsigned:



template <typename K>

class Person<unsigned, K> {

    // ..........

};



То есть определяется только значение для параметра T - это тип unsigned. Значение параметра K по прежнему остается неизвестным. И в этом случае после ключевого слова template указываем неустановленные параметра (в данном случае параметр K), для которых значение неизвестно.

Угловые скобки после названия класса class Person<unsigned, K> указывают как специализируются параметры шаблона. Список здесь должен иметь то же количество параметров, что и в исходном неспециализированном шаблоне. Первый параметр для этой специализации — unsigned. Другой параметр указывается как соответствующее имя параметра в оригинальном шаблоне.

Таким образом, если первым среди параметров шаблонов указывается unsigned, то для создания класса компилятор использует частичную специализацию:



Person<unsigned, std::string> tom{"Tom", "+4444444444"};

tom.print();    // Id: 1   Name: Tom       Phone: +4444444444


Person<unsigned, std::string> sam{"Sam", "+555555555"};

sam.print();    // Id: 2   Name: Sam       Phone: +555555555



Если первым значением для параметров шаблона указан тип, отличный от unsigned, тогда компилятор полностью сам генерирует определение класса:



Person<std::string, std::string> bob{"Bob", "+1234567688"};    // T - std::string

bob.setId("13");

bob.print();    // Id: 13  Name: Bob       Phone: +1234567688



Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования