|
||||
|
Замечания по поводу платформ STL от Microsoft В начале книги я ввел термин «платформа STL», означающий комбинацию конкретного компилятора и конкретной реализации STL. Различие между компилятором и библиотекой особенно важно при использовании компилятора Microsoft Visual C++ версий 6 и ниже (то есть компилятора, входившего в комплект поставки Microsoft Visual Studio версий 6 и ниже), поскольку компилятор иногда способен на большее, чем прилагаемая реализация STL. В настоящем приложении описаны важные недостатки старых платформ STL от Microsoft и предложены обходные решения, делающие работу на этих платформах значительно более удобной. Дальнейший материал предназначен для разработчиков, использующих Microsoft Visual C++ (MSVC) версий 4-6. В Visual C++ .NET перечисленные проблемы отсутствуют. Шаблоны функций классов в STL Допустим, у вас есть два вектора объектов Widget, требуется скопировать объекты Widgetиз одного вектора в конец другого. Задача решается легко — достаточно воспользоваться интервальной функцией insertконтейнера vector: vector<Widget> vw1, vw2; … vw1.insert(vw1.end(), vw2.begin(), vw2.end()); // Присоединить к vw1 копию // объектов Widget из vw2 Аналогичную операцию можно выполнить с контейнерами vectorи deque: vector<Widget> vw; deque<Widget> dw; … vw.insert(vw.end(), dw.begin(), dw.end()); // Присоединить к vw копию // объектов Widget из dw Оказывается, эту операцию можно выполнить независимо от того, в каких контейнерах хранятся копируемые объекты. Подходят даже нестандартные контейнеры: vector<Widget> vw; … list<Widget> lw; … vw.insert(vw.begin(), lw.begin(), lw.end()); // Присоединить к vw копию // объектов Widget из lw set<Widget> sw; … vw.insert(vw.begin(), sw.begin(), sw.end()); // Присоединить к vw копию // объектов Widget из sw template<typename T, typename Allocator = allocator<T> > // Шаблон нестандартного class SpecialContainer{…}; // STL-совместимого контейнера SpecialContainer<Widget> scw; vw.insert(vw.begin(), scw.begin(), scw.end()); // Присоединить к vw копию // объектов Widget из scw Подобная универсальность объясняется тем, что интервальная функция insertконтейнера rangeвообще не является функцией в общепринятом смысле. Это шаблон функции контейнера, специализация которого с произвольным типом итератора порождает конкретную интервальную функцию insert. Для контейнера vectorшаблон insertобъявлен в Стандарте следующим образом: template <class T, class Allocator = allocator<T> > class vector { public: … template<class InputIterator> void insert(iterator position, InputIterator first, InputIterator last); }; Каждый стандартный контейнер должен поддерживать шаблонную версию интервальной функции insert. Аналогичные шаблоны также обязательны для интервальных конструкторов и для интервальной формы assign(см. совет 5). MSVC версий 4-6 К сожалению, в реализации STL, входящей в комплект поставки версий 4-6, шаблоны функций не объявляются. Библиотека изначально разрабатывалась для MSVC версии 4, а этот компилятор, как и большинство компиляторов того времени, не обладал поддержкой шаблонов функций классов. При переходе от MSCV4 к MSVC6 поддержка этих шаблонов была включена в компилятор, но вследствие судебных дел, косвенно затрагивавших фирму Microsoft, библиотека оставалась практически в неизменном состоянии. Поскольку реализация STL, поставляемая с MSVC4-6, предназначалась для компилятора без поддержки шаблонов функций классов, авторы библиотеки имитировали эти шаблоны и заменили их конкретными функциями, которым при вызове передавались итераторы контейнера соответствующего типа. Например, шаблон insertбыл заменен следующей функцией: void insert(iterator position, // "iterator" - тип итератора iterator first, iterator last); // для конкретного контейнера Эта ограниченная форма интервальных функций позволяла выполнить интервальную вставку из vector<Widget>в vector<Widget>или из list<int>в list<int>, но смешанные операции (например, вставка из vector<Widget>в list<Widget>или из set<int>в deque<int>) не поддерживались. Более того, не поддерживалась даже интервальная вставка (а также конструирование или assign) из vector<long>в vector<int>, поскольку итераторы vector<long>::iteratorи vector<int>::iteratorотносятся к разным типам. В результате следующий фрагмент, принимаемый другими компиляторами, не компилируется в MSVC4-6: istream_iterator<Widget> begin(cin), end; // Создать итераторы begin и end // для чтения объектов Widget // из cin (см. совет 6). vector<Widget> vw(begin, end); // Прочитать объекты Widget // из cin в vw (см. совет 6) // не компилируется в MSVC4-6! list<Widget> lw; lw.assign(vw.rbegin(), vw.rend()); // Присвоить lw содержимое vw // (в обратном порядке); // не компилируется в MSVC4-6! SpeciаlContainer<Widget> scw; scw.insert(scw.end(), lw.begin(), lw.end()); // Вставить в конец scw // копию объектов Widget из lw; // не компилируется в MSVC4-6! Так что же делать, если вы работаете в среде MSVC4-6? Это зависит от используемой версии MSVC и того, вынуждены ли вы использовать реализацию STL, поставляемую вместе с компилятором. Обходное решение для MSVC4-5 Еще раз посмотрим на правильный код, который не компилируется для реализации STL из поставки MSVC4-6: vector<Widget> vw(begin, end); // Отвергается реализацией STL // из поставки MSVC4-6 list<Widget> lw; … lw.assign(vw.rbegin(), vw.rend()); // То же SpeciаlContainer<Widget> scw; … scw.insert(scw.end(), lw.begin(), lw.end()); // То же Несмотря на внешние различия, выделенные вызовы отвергаются компилятором по одной и той же причине: из-за отсутствия шаблонов функций класса в реализации STL. Соответственно и решение во всех случаях оказывается одним и тем же: замена вызовом copyс итератором вставки (см. совет 30). Ниже приведены обходные решения для всех примеров, приведенных ранее: istream_iterator<Widget> begin(cin), end; vector<Widget> vw(begin, end); //Создать vw конструктором copy(begin, end, back_inserter(vw)); // по умолчанию и скопировать // в него объекты Widget из cin list<Widget> lw; … lw.clear(); // Удалить из lw старые объекты; copy(vw.rbegin(), vw.rend(), // скопировать объекты из vw back_inserter(lw)); //(в обратном порядке) SpecialContainer<Widget> scw; copy(lw.begin(), lw.end(), // Скопировать объекты Widget inserter(scw, scw.end())); // из lw в конец scw Я рекомендую использовать эти обходные решения с библиотекой, входящей в комплект поставки MSVC4-5. С другой стороны, будьте внимательны и не забывайте о том, что эти решения являются обходными. Как показано в совете 5, алгоритм copyпочти всегда уступает интервальной функции контейнера, поэтому как только представится возможность обновить платформу STL до версии с поддержкой шаблонов функций класса, откажитесь от использования copyв тех местах, где следовало бы использовать интервальные функции. Обходное решение для MSVC6 Обходное решение из предыдущего раздела подходит и для MSVC6, но в этом случае существует и другой вариант. Компиляторы MSVC4-5 не обладают полноценной поддержкой шаблонов функций класса, поэтому отсутствие этих шаблонов в реализации STL несущественно. В MSVC6 дело обстоит иначе, поскольку компилятор этой среды поддерживает шаблоны функций класса. Таким образом, возникает естественное желание заменить реализацию STL из поставки MSVC6 другой реализацией с шаблонами функций классов, предписанными Стандартом. В совете 50 упоминаются свободно распространяемые реализации STL от SGI и STLport; в списках поддерживаемых компиляторов обеих реализаций упоминается MSVC6. Кроме того, можно приобрести новейшую MSVC-совместимую реализацию STL от Dinkumware. У каждого из этих вариантов есть свои достоинства и недостатки. Реализации SGI и STLport распространяются бесплатно, поэтому какая-либо официальная поддержка в этих случаях попросту отсутствует. Более того, поскольку реализации SGI и STLport рассчитаны на работу с разными компиляторами, вам придется дополнительно настроить их для обеспечения максимального быстродействия в MSVC6. В частности, может потребоваться включение поддержки шаблонов функций классов — из-за совместимости с большим количеством разных компиляторов в SGI и/или STLport эта поддержка отключена по умолчанию. Возможно, также придется позаботиться о компоновке с другими библиотеками MSVC6 (особенно DLL), проследить за использованием соответствующих версий для отладки и т. д. Если подобные вещи вас пугают или вы руководствуетесь принципом «бесплатные программы обходятся слишком дорого», рассмотрите альтернативную реализацию STL для MSVC6 от Dinkumware. Библиотека проектировалась с расчетом на максимальную простоту замены и на соответствие Стандарту. Реализация STL из MSVC6 разрабатывалась именно в Dinkumware, поэтому вполне возможно, что новая реализация STL действительно легко заменяет оригинал. За дополнительной информацией о реализациях STL от Dunkumware обращайтесь на сайт компании http://www.dunkumware.com. Независимо от того, на какой реализации вы остановите свой выбор, вы получите нечто большее, чем STL с шаблонами функций классов. В альтернативных реализациях будут решены проблемы соответствия Стандарту в других областях — скажем, отсутствие объявления push_backв контейнере string. Более того, в вашем распоряжении окажутся полезные расширения STL, в том числе хэшированные контейнеры (см. совет 25) и односвязные списки (контейнер slist). Реализации SGI и STLport также содержат множество нестандартных классов функторов, включая select1stи select2nd(см. совет 50). Но даже если вы вынуждены работать с реализацией STL из поставки MSVC6, сайт Dunkumware все же стоит посетить. На нем перечислены известные ошибки в реализации библиотеки MSVC6 и приведены рекомендации относительно того, как модифицировать библиотеку для сокращения ее недостатков. Не стоит и говорить, что редактирование заголовочных файлов библиотеки — дело весьма рискованное. Если у вас возникнут проблемы, не вините в них меня. |
|
||
Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное |
||||
|