Онлайн библиотека PLAM.RU


  • Шаблоны функций классов в STL
  • MSVC версий 4-6
  • Обходное решение для MSVC4-5
  • Обходное решение для MSVC6
  • Замечания по поводу платформ 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 и приведены рекомендации относительно того, как модифицировать библиотеку для сокращения ее недостатков. Не стоит и говорить, что редактирование заголовочных файлов библиотеки — дело весьма рискованное. Если у вас возникнут проблемы, не вините в них меня.









    Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное

    Все материалы представлены для ознакомления и принадлежат их авторам.