Здравствуйте. Начал изучать шаблоны. Пока всё было просто и понятно, пока не дошёл до явной специализациии и явного создания экземпляра.
Например, есть такой шаблон
template
void Swap(T&, T&);
Для этого шаблона можно создать явную специализацию для какого-то определённого типа, например
template<>
void Swap(MyStruct&, MyStruct&);
Это объявление даст команду компилятору создать экземпляр функции Swap для типа MyStruct и в коде
MyStruct a, b;
Swap(a, b);
Будет вызван именно этот экземпляр функции.
Теперь про явное создание экземпляра. Пусть у нас сеть этот же шаблон. Тогда в этом коде
double a, b;
Swap(a, b);
Либо в этом
double a, b;
template void Swap(double&, double&);
Swap(a, b);
Компилятору тоже будет указано создать экземпляр функции для типа double.
А значит, если это одно и тоже, то можно использовать что-то одно?
Или, если я чего-то не понимаю, объясните мне пожалуйста, в чём разница между явной специализацией и явным созданием экземпляра?
Ответ
Смотрите.
Явная специализация — это метод сказать компилятору: когда будешь компилировать Swap для T = MyStruct, пользуйся не обычным определением, а специальным. Специальное поведение нужно, например, если для конкретного типа аргумента вы можете что-то сделать оптимальнее. Например, vector использует особую стратегию хранения, отличную от общего случая, чтобы упаковать значения в bitfield.
Теперь о явном создании экземпляра. Начнём с того, что оно, говоря в общем, не нужно. Давайте разберём, что происходит, когда вы определяете template.
Компилятор не может скомпилировать шаблон сам по себе. Например, если вы в шаблоне вызываете метод класса-параметра шаблона, компилятор не знает, какие у него аргументы на самом деле и каков возвращаемый тип. Поэтому компилятор может лишь скомпилировать отдельные специализации шаблонного класса.
Но в отличие от обычного класса, который существует в единственном раз и навсегда заданном варианте, специализаций шаблонного класса (или функции) существует потенциально бесконечно много: у вас может быть vector, vector, vector, vector и т. д.
Вследствие этого компилятор, видя объявление методов шаблонного класса, вовсе не генерирует никакого кода! (Не генерировать же ему и вправду все возможные специализации!) Возникает закономерный вопрос: а когда же генерируется код, и для каких специализаций? Ответ на это таков: в тот момент, когда компилятор видит обращение к шаблонному классу (например, видит vector), он приостанавливает компиляцию и тихонько в уголке компилирует нужную специализацию шаблона.
Это значит, кстати, что код для vector будет находиться в каждом объектном файле, использующем vector. Нужна специальная магия компоновщика, чтобы выкинуть дублирующийся код и не получить сообщение об ошибке "multiply defined symbol".
Вернёмся к нашей теме: а для чего всё-таки тогда нужно явное создание экземпляра? А вот для чего. Допустим, ваша библиотека предоставляет пользователям шаблонный класс. Если вы нигде в своей библиотеке не пользуетесь этим классом, а лишь определяете его, компилятор, понятно, не сгенерирует вообще никакого кода!
Вы, однако, можете сделать явное создание для некоторых специализаций экземпляра в своём коде (то самое явное создание). Таким образом вы заставите компилятор таки сгенерировать код для него — точнее, для выбранных вами специализаций.
Для чего это нужно? Этим вы по идее увеличите скорость компиляции для ваших клиентов. Если компилятор увидит, что есть явная специализация, он сможет по идее не отвлекаться на кодогенерацию специализации шаблона каждый раз, видит такую специализацию у клиента.
Кроме того, если вы положите имплементацию шаблона не в .h, как принято, а в .cpp, и проведёте там явную специализацию, ваши клиенты смогут пользоваться шаблоном только с типами, для которых вы эту самую специализацию сделали. (Не уверен, однако, что это — хорошее применение.)
Кстати, вы можете вызывать вашу функцию Swap и без явного указания шаблонного типа:
double a = 2, b = 4;
Swap(a, b);
Компилятор умный, он догадается сам.
Комментариев нет:
Отправить комментарий