Программирование >>  Обобщенные обратные вызовы 

1 ... 8 9 10 [ 11 ] 12 13 14 ... 84


дартной библиотеки; к тому же это зачастую дает более безопасную с точки зрения исключений функцию.

> Рекомендация

Подумайте о специализации std:: swap для ваших типов, если для них возможен более эффективный способ обмена, чем стандартный.

2 Уничтожение и восстановление. Идея этого способа заключается в обмене с использованием копирующего конструктора т вместо оператора копирующего присваивания; конечно, этот способ работает, только если т имеет конструктор копирования.

пример 6-2(г): swap без присваивания.

template <class т>

void swap С т& а, т& b ) {

if( &а != &b ) { примечание: эта проверка

т tempC а ); destroyC &а ); constructC &а, b ); destroyC &b ); constructC &b, temp );

теперь необходима!

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

Если мы знаем, что конструктор копирования гарантированно не генерирует исключений, то данная версия имеет то преимущество, что позволяет работать с типами, которые не имеют оператора присваивания, но могут быть сконструированы путем копирования, а такие типы вовсе не являются редкостью. Впрочем, возможность обмена для таких типов отнюдь не обязательно считается достоинством. Если тип нельзя присваивать, то, вероятно, на то есть существенные причины, например, если он не имеет семантики значения, но имеет константные или ссылочные члены, то механизм, который обеспечивает (или даже навязывает) типу семантику значения, может приводить к неожиданным и некорректным результатам.

Что еще хуже, этот подход начинает игру с временем жизни объектов, а это всегда чревато неприятностями. Здесь под игрой я подразумеваю, что при этом изменяются не только значения, но и само существование объектов, с которыми работает данная функция. Код, использующий функцию swap из примера 6-2(г), может легко привести к неожиданным результатам, если пользователь забудет о необычной семантике уничтожения.

Небольшая рекомендация. Если вы вынуждены играть со временем жизни объектов и знаете, что здесь все в порядке, и вы точно знаете, что конструкторы копирования объектов, с которыми вы работаете, никогда не генерируют исключений, и вы абсолютно убеждены, что навязанная семантика значений для данных объектов в вашем приложении вполне допустима, то тогда (и только тогда) вы можете оправданно использовать описанный подход в конкретных перечисленных ситуациях. Но даже при этом не делайте такую операцию универсальным шаблоном, который может быть случайно использован для любого другого типа, и четко документируйте ее, чтобы никто случайно не воспользовался ею в другой, не столь благоприятной ситуации, потому что в контексте С++ эта методика выглядит очень экзотично.



Задача 7. Почему не специализируются

шаблоны функций? Сложность: 8

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

Вопрос для новичка

1. Какие два основных вида шаблонов имеются в С++, и как они могут быть специализированы?

Вопрос для профессионала

2. Какая из версий функции f будет вызвана в последней строке приведенного кода? Почему?

tempiate<class т> void f( т );

tempi ateo

void f<int*>( int* );

tempiate<class T> void f( T* );

int *p;

f( p ); какая функция будет вызвана?

Решение

Перегрузка и специализация

Очень важно убедиться в правильном использовании терминологии при обсуждении данного вопроса, поэтому сначала - небольшой обзор.

1. Какие два основных вида шаблонов характерны для С++ и как они могут быть специализированы?

В С++ предусмотрено два типа шаблонов - шаблоны классов и шаблоны функций. Эти два типа шаблонов работают не совсем одинаково, и наиболее очевидное отличие заключается в перегрузке (overloading): обычные классы С++ не могут быть перегружены, так что не могут быть перегружены и их шаблоны. С другой стороны, функции в С+ + , имеющие одинаковые имена, могут быть перегружены, так что могут бьпъ перегружены и шаблоны функций. Это вполне понятно и естественно, что демонстрируется следующим примером.

Пример 7-1: шаблоны классов и функций и перегрузка шаблон класса

tempiate<class т> class х { /* ...*/ }; (а)

Шаблон функции с перегрузкой

tempiate<class т> void f( т ); (б)

tempiate<class T> void f( int, T, double ); (в)

Такие неспециализированные шаблоны называются также первичными шаблонами (primary templates).



Первичные шаблоны могут быть специализированы. В этом шаблоны классов и шаблоны функций существенно отличаются, как вы увидите далее в данной задаче. Шаблон класса может быть частично (partially) или полностью специализирован (fully specialized). Шаблон функции может быть специализирован только полностью, но, поскольку шаблоны функции могут быть перегружены, мы можем получить при помощи перегрузки тот же эффект, что и при помощи частичной специализации. Эти отличия продемонстрированы в следующем примере.

Пример 7-1, продолжение: специализированные шаблоны

частичная специализация (а) для указателей tempiate<class т> class х<т*> {/*...*/};

полная специализация (а) для типа int templateo class X<int> { /* ... */ };

Отдельный первичный шаблон, перегружающий (б) and (в) - не частичная специализация (б), поскольку понятия частичной специализации шаблона функции не существует! tempiate<c1ass т> void f( т* ); (г)

Полная специализация (б) для типа int

templateo void f<int>( int ); (д)

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

void f( double ); (е)

> Рекомендация

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

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

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

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

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

В стандарте полная специализация называется явной специализацией (explicit specialization).



1 ... 8 9 10 [ 11 ] 12 13 14 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика