Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 119 120 121 [ 122 ] 123 124 125 ... 144


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

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

П1аблон класса или функции можно специализировать. Например, если есть шаблон

template<class Т> class vector { . . .

Т& operator[](int i);

то .можно ввести специализации, то есть отдельные объявления, скажем, для vector<char> или vector<complex>::operator[] (int):

class vector<char> { ...

char& operator[](int i);

complex& vector<complex>::operator[](int i) {/*...*/ }

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

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

template<class Т> class X {

Т V; public:

Т read О const ( return v; ) void write(int w) ( v = vv; }



void f(X<int> г) {

r.write(2) ;

int i = r.read();

Кажется естественным предположить, что f () использует определенную выше функцию-член. Но это не гарантируется. В какой-то другой единице трансляции могла бы быть определена функция X<int>: : write (), которая делает нечто совершенно иное.

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

Я счел, что специализация в своем первоначальном виде реализована неудачно, но при этом предоставляет существенную функциональность. Как же можно сохранить эту функциональность, устранив все недостатки? После многих сложных рассуждений я предложил очень простое решение, которое было одобрено на заседании в Сан-Хосе: специализация должна быть объявлена перед использованием. Это ставит ее в один ряд с правилами обычной перегрузки. Если в области действия, где специализация используется, не видно ее объявления, то применяется обычное определение шаблона. Например:

template<class Т> void sort(vector<T>& v) { /* ... */ }

void sort<char*>(vector<char*>& v); специализация

void f(vector<char*>& vl, vector<String>& v2) {

sort(vl); используется специализация sort (vector<char*>5c)

sort(v2); используется общий шаблон

sort(vector<T>&), где Т - это String

void sort<String>(vector<String>& v); ошибка: специализация

после использования

void sorto(vector<double>& v); правильно: sort(double)

еще не использовалась

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

specialise void sort(vector<String>&);

но ко.митет на заседании в Сан-Хосе был настроен решительно против новых ключевых слов. К то.му же на этом собрании, где присутствовали люди разных



национальностей, мы никогда не смогли бы прийти к единому мнению о том, как надо правильно писать: specialise или specialize.

15.10.4. Нахождение определений шаблонов

Традиционно программа на С++, как и на С, представляет собой множество файлов, которые собираются в единицы трансляции, компилируются и связываются с помощью набора различных програ.мм, работающих на основе общих соглашений. Например, файлы с расширением . с - это исходные тексты; они включают . h-файлы для получения информации о других частях программы. Из . с-файлов компилятор генерирует объектные файлы, обычно имеющие расширение . о. Исполняемая программа получается путем связывания всех . о-файлов. Архивы и динамические подключаемые библиотеки несколько усложняют дело, но не из-.меняют картины в целом.

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

На этот счет в ARM не было однозначных указаний разработчикам (см. раздел 15.10), в результате появилось множество разных схем, которые препятствовали переносимости. Для некоторых компиляторов требовалось, чтобы шаблоны находились в . h-файлах. Это может негативно отразиться на производительности, поскольку в каждую единицу трансляции входит слишком много информации и каждая единица становится зависимой от всех шаблонов, входящих в ее . h-файлы. Вообще-то шаблоны не являются частью заголовочных файлов. Другие компиляторы требуют, чтобы шаблоны находились в . с-файлах. Это усложняет нахождение определения шаблона функции, когда его надо инстанцировать, а также синтез контекста для инстанцирования.

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

Можно представлять себе репозитарий как хранящуюся на диске таблицу символов, в которой для каждого шаблона есть один элемент. Компилятор использует ее для получения информации об объявлениях, определениях, специализациях, использовании и т.д. С учетом такой концепции можно охарактеризовать .модель инстанцирования следующим образом: поддерживает все языковые средства, согласуется с применяемыми принципами использования .h и .с-файлов, не требует от пользователя знаний о репозитарий и предоставляет возможности для проверки ошибок, оптимизации и повышения эффективности компиляции



1 ... 119 120 121 [ 122 ] 123 124 125 ... 144

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