|
Программирование >> Полиморфизм без виртуальных функций в с++
template<class Т> class Ptr { указатель на Т Т* tp; Ptr(Т*); friend template<class Т2> class Ptr<T2>; public: template<class T2> operator Ptr<T2> {); . . . Шаблоны-члены описаны в разделе 15.9.3. 15.9.2. Преобразования с вышеописанной проблемой тесно связана другая: не существует единого способа определить преобразования между различными классами, сгенериро-ванны.ми из одного и того же шаблона. Рассмотрим, например, шаблон complex, который определяет комплексные числа для некоторого множества скалярных типов: template<class scalar> class complex { scalar re, im; public: . .. Мы може.м использовать complex<f loat>, complex<double> и т.д., но при этом желательно, чтобы существовало преобразовыванпе из типа complex с низкой точностью в тип complex с высокой точностью. Например: complex<double> sqrt{complex<double>); complex<float> cl(1.2f,6.7f); complex<double> c2 = sqrt(cl); ошибка: несоответствие типов: ожидается complex<double> Было бы неплохо, если бы существовал какой-то способ сделать вызов sqrt допустимым. Для этого программисты отказываются от шаблонов в пользу дублирования определения классов: class float complex { float re, im; public: . . . class double complex { double re, im; public: double complex{float complex c) : re(c.re), im{c.im) (} . .. Цель такого дублирования - определить конструктор, который и задает преобразование. И в этом случае, на мой взгляд, возможны только решения, требуюшие вложенных шаблонов в сочетании с той или иной формой ограничений. Фактически ограничение может быть неявным: template<class scalar> class complex { scalar re, im; public: template<class T2> complex(const complex<T2>& c) : re(c.re), im(c.im) { } ... Другими словами, сконструировать complex<Tl> из complex<T2> удается только в том случае, когда есть возможность инициализировать Т1 с по.мощью Т2. Это представляется разу.мным. Такое определение включает и обычный копирующий конструктор. В данной ситуации приведенный выше пример с sqrt () становится законным. К сожалению, определение допускает и сужающие преобразования комплексных чисел просто потому, что в С-ы- допустимы сужающие преобразования для скаляров. Естественно, если принять такое определение complex, то компилятор, предупреждающий о сужающих преобразованиях для скаляров, будет предупреждать и о сужающих преобразованиях для значений типа complex. Мы можем получить привычные имена с помощью typedef: typedef complex<float> float complex; typedef complex<double> double complex; typedef complex<long double> long double complex; Думается, что варианты без typedef читаются лучше. 15.9.3. Шаблоны-члены Единственная причина, по которой, согласно ARM, шаблоны не .могут являться членами классов, - я не смог убедить себя, что такую возможность будет легко реализовать. Шаблоны-члены входили в первоначальный проект шаблонов, и в целом я за то, чтобы любые конструкции, вводящие область действия, имели вложенные формы (см. разделы 3.12 и 17.4.5.4). Но если бы я безо всяких ограничений разрешил в С-ы- шаблоны-члены, то нечаянно разрушил бы .модель размещения объекта в памяти, что отбросило бы нас немного назад. Рассмотрим решение задачи двойной диспетчеризации (см. раздел 13.8.1): class Shape { . . . template<class Т> virtual Bool intersect(const T&) const =0; class Rectangle : public Shape { ... template<class T> virtual Bool intersect(const T& s) const; 15.10. Инстанцирование шаблонов Первоначально ([Stroustrup, 1988b], [ARM]) в C-i-i- не было оператора для инстанцирования шаблона, то есть операции явного генерирования объявления класса и определений функций для конкретного набора аргументов шаблона. Лишь имея полную програ.мму, можно узнать, какие шаблоны следует инстанцировать. Часто они определяются в библиотеках, а инстанцирование производится в результате действий пользователей, даже не подозревающих о наличии таких средств. Поэто.му запрос инстанцирования, например, с по.мощью оператора new из Ada или подобного ему пользователю представлялся неразумным. Более того, если бы существовал оператор инстанцирования шаблона, он должен был бы грамотно обрабатывать случай, когда две не связанных между собой части программы требуют инстанцировать одну и ту же функцию с одни.м и тем же наборам аргументов. В этом случае приходилось бы избегать дублирования кода и не потерять возможность динамического связывания. В ARM приводятся комментарии на данную тему, но окончательного ответа не дается: Решение о том, какие функции генерировать из определения шаблона функции, нельзя принять, пока нет полной программы, то есть до тех пор, пока не станет ясно, какие функции нужны. Принято, что обнаружение ошибок отклодывается до последнего момента: когда после начальной стадии компоновки уже сгенерированы определения из шаблонов функций. Но многим может показаться, что это слишком поздно. Кроме того, правило возлагает максимальную ответственность на среду программирования. Именно системе поручается найти определения шаблонов классов, шаблонов функций и классов, необходимых для генерирования определений функций по шаблонам. Для некоторых сред это слишком сложно. template<class Т> virtual Bool Rectangle::intersect(const T& s) const { return s.intersect(*this); *this - это Rectangle разрешаем в зависимости от s Этот пример просто обязан быть незаконным, иначе пришлось бы вводить дополнительный элемент в таблицу виртуальных функций для класса Shape всякий раз, как кто-то вызовет Shape: : intersect () с новым типом аргумента. Это означало бы, что создавать таблицы виртуальных функций и размещать в них указатели на функции может только компоновщик. Поэтому шаблон-член не может быть виртуальным. Я обнаружил это уже после выхода ARM, так что ограничение, согласно которому шаблоны можно определять только в глобальной области действия, оказалось просто спасением. С другой стороны, упомянутые в это.м разделе проблемы преобразования из-за отсутствия шаблонов-членов не имеют решения. Шаблоны-члены одобрены комитетом на заседании в Сан-Диего в марте 1994 г. Во многих случаях альтернативой вложенным шаблонам классов служит явное задание аргументов шаблонов функций (см. раздел 15.6.2).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |