Шаблоны и STL - PullRequest
       10

Шаблоны и STL

5 голосов
/ 10 февраля 2011

Следующий код представляет контейнер на основе std :: vector

template <typename Item>
struct TList
{
    typedef std::vector <Item> Type;
};


template <typename Item>
class List
{
private
            typename TList <Item>::Type items;
    ....
}

int main()
{
  List <Object> list;
}

Возможно ли шаблонизировать std :: vector и создать общий контейнер, что-то в этом роде?

template <typename Item, typename stl_container>
struct TList
{
    typedef stl_container<Item>;
};

где stl_container представляет собой std :: vector, std :: list, std :: set ...?Я хотел бы выбрать тип контейнера во время создания.

List <Object, std::vector> list; //vector of objects, not a real code
List <Object, std::vector> list; //list of objects, not a real code

Спасибо за ваши ответы ...

Обновленный вопрос:

Я попробовал следующий код, но есть ошибки:

#include <vector>
template <typename Item, typename Container>
struct TList
{
   typedef typename Container <Item>::type type; //Error C2059: syntax error : '<', Error C2238: unexpected token(s) preceding ';
};


template <typename T>
struct vector_container
{
  typedef std::vector<T> type;
};

int _tmain(int argc, _TCHAR* argv[])
{
TList <int, vector_container> v;
TList <int, map_container> m;
}

Ответы [ 6 ]

11 голосов
/ 11 февраля 2011

Да, но не напрямую:

template <typename Item, template <typename> class Container>
struct TList
{
    typedef typename Container<Item>::type type;
};

Тогда вы можете определить различные политики контейнеров:

template <typename T>
struct vector_container
{
    typedef std::vector<T> type;
};

template <typename T>
struct map_container
{
    typedef std::map<T, std::string> type;
};

TList<int, vector_container> v;
TList<int, map_container> m;

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

Однако с C ++ 0x мы можем сделать это просто прекрасно:

#include <map>
#include <vector>

template <typename Item,
            template <typename...> class Container, typename... Args> 
struct TList
{
    // Args lets the user specify additional explicit template arguments
    Container<Item, Args...> storage;
};

int main()
{
    TList<int, std::vector> v;
    TList<int, std::map, float> m;
}

Отлично.К сожалению, нет никакого способа воспроизвести это в C ++ 03, кроме как через классы политики косвенного обращения, представленные, как описано выше.


* Я хочу подчеркнуть, что под «немного многословным» я имею в виду, что этонеортодоксальные».Правильное решение вашей проблемы - то, что делает стандартная библиотека, , как объясняет Джерри .Вы просто позволяете пользователю вашего адаптера контейнера указать весь тип контейнера напрямую:

template <typename Item, typename Container = std::vector<Item>>
struct TList
{};

Но это оставляет большую проблему: что, если я не хочу, чтобы тип значения контейнера был Itemно something_else<Item>?Другими словами, как я могу изменить тип значения существующего контейнера на что-то другое?В вашем случае это не так, поэтому читайте дальше, но в случае, если мы это сделаем, мы хотим перепривязать контейнер.

К сожалению для нас, контейнеры не имеют этогофункциональность, хотя распределители делают:

template <typename T>
struct allocator
{
    template <typename U>
    struct rebind
    {
        typedef allocator<U> type;
    };

    // ...
};

Это позволяет нам получить allocator<U> при allocator<T>.Как мы можем сделать то же самое для контейнеров без этой навязчивой утилиты?В C ++ 0x это просто:

template <typename T, typename Container>
struct rebind; // not defined

template <typename T, typename Container, typename... Args>
struct rebind<T, Container<Args...>>
{
    // assumes the rest are filled with defaults**
    typedef Container<T> type; 
};

Учитывая std::vector<int>, мы можем, например, выполнить rebind<float, std::vector<int>>::type.В отличие от предыдущего решения C ++ 0x, его можно эмулировать в C ++ 03 с помощью макросов и итераций.


** Обратите внимание, что этот механизм можно сделать гораздо более мощным, например, указав, какие аргументысохранить, что связать, что связать себя, прежде чем использовать в качестве аргументов и т. д., но это оставлено в качестве упражнения для читателя.:)

6 голосов
/ 11 февраля 2011

Я немного озадачен, почему некоторые очень умные (и компетентные) люди говорят нет.

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

Например, std::stack использует какой-то другой контейнер (например, std::deque, std::list или std::vector) для хранения объектов, а сам std::stack просто обеспечивает упрощенный / ограниченный интерфейс, когда вы просто хочу использовать операции стека. Базовый контейнер, который будет использоваться std::stack, предоставляется в качестве параметра шаблона. Вот как выглядит код в стандарте:

namespace std {
    template <class T, class Container = deque<T> >
    class stack {
    public:
        typedef typename Container::value_type value_type;
        typedef typename Container::size_type  size_type;
        typedef Container                      container_type;
    protected:
        Container c;
    public:
        explicit stack(const Container& = Container());
        bool empty() const             { return c.empty(); }
        size_type size() const         { return c.size(); }
        value_type& top()              { return c.back(); }
        const value_type& top() const  { return c.back(); }
        void push(const value_type& x) { c.push_back(x); }
        void pop()                     { c.pop_back(); }
    };
}

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

5 голосов
/ 10 февраля 2011

Да и нет.

Вы можете использовать параметр шаблона шаблона, например,

template <typename Item, template <typename> class Container>
struct TList { /* ... */ };

Однако, как правило, параметры шаблона шаблона не особенно полезны, так как число и типыпараметры шаблона должны совпадать.Таким образом, вышеприведенное не будет соответствовать std::vector, поскольку на самом деле оно имеет два параметра шаблона: один для типа значения и один для распределителя.Параметр шаблона шаблона не может использовать никаких аргументов шаблона по умолчанию.

Чтобы использовать шаблон std::vector в качестве аргумента, TList должен быть объявлен как:

template <typename Item, template <typename, typename> class Container>
struct TList { /* ... */ };

Однако с этим шаблоном вы не сможете использовать шаблон std::map в качестве аргумента, поскольку он имеет четыре шаблонных параметра: типы ключей и значений, тип распределителя итип компаратора.

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

2 голосов
/ 11 февраля 2011

ну, вы можете взломать его с помощью макроса:

template <typename T, typename stl_container = std::vector<T> >
struct TList
{
    typedef stl_container Type;
};

#define TLIST(T, C) TList<T, C<T> >

TList<int> foo;
TList<int, std::list<int> > bar;
TLIST(int, std::list) baz;
0 голосов
/ 11 февраля 2011

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

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

template < typename T > struct simple_vector : std::vector<T> {};

Или вы можете использовать шаблонную идиому typedef:

template < typename T > struct retrieve_vector { typedef std::vector<T> type; };
0 голосов
/ 11 февраля 2011

Можно ли шаблонизировать std :: vector и создать общий контейнер, что-то в этом роде?

Нет. Вам придется шаблонизировать функцию или объект , используя контейнер - вы не можете шаблонизировать сам контейнер.

Например. рассмотрим типичный std::find:

template<class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value )
{
    for ( ;first!=last; first++) if ( *first==value ) break;
    return first;
}

Это работает для любого контейнера, но вообще не нуждается в tempalte с контейнером.

Кроме того, учитывая то, что вы пытаетесь создать независимый от контейнера код, вы можете купить или одолжить себе копию Scott Meyers ' Effective STL и прочитайте пункт 2: остерегайтесь иллюзии независимого от контейнера кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...