Как «сопоставить шаблон» с шаблоном? - PullRequest
5 голосов
/ 26 марта 2011

Обычно в шаблонах вы хотите знать весь тип, но в моем случае мне нужно знать больше и хотеть «разбить» тип. Возьмите этот пример:

template <typename Collection<typename T> >
T get_front(Collection const& c)
{
  return c.front();
}

Как мне этого добиться? Примечание: мне нужно, чтобы он автоматически выводил типы, а не передавал что-то вроде <std::vector<int>, int>

Ответы [ 4 ]

7 голосов
/ 26 марта 2011

Редактировать : путь C ++ 0x можно найти в конце.
Редактировать 2 : Я тупой, и способ Более короткий путь C ++ 98/03, чем все эти черты, можно найти в конце ответа.

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


Дело в том, что разные контейнеры принимают разное количество параметров шаблона.Например, std::vector, std::deque и std::list: 2: базовый тип элемента T и тип распределителя Alloc.std::set и std::map, с другой стороны, принимают 3 и 4 соответственно: оба имеют тип ключа K, map принимает другой тип значения V, затем оба принимают тип компаратора Compare и тип распределителя Alloc.Вы можете получить обзор всех типов контейнеров, предоставляемых стандартной библиотекой, например, здесь .


Теперь для шаблонных пистолетов.Мы будем использовать частично специализированные метаструктуры признаков, чтобы получить базовый тип элемента.(Я использую class вместо typename из чистого предпочтения.)

template<class T>
struct ContainerTraits;

// vector, deque, list and even stack and queue (2 template parameters)
template<
    template<class, class> class Container,
    class T, class Other
>
struct ContainerTraits< Container<T,Other> >{
    typedef T value_type;
};

// for set, multiset, and priority_queue (3 template parameters)
template<
    template<class, class, class> class Container,
    class T, class Other1, class Other2
>
struct ContainerTraits< Container<T,Other1,Other2> >{
    typedef T value_type;
};

// for map and multimap (4 template parameters)
template<
    template<class, class, class, class> class Container,
    class Key, class T, class Other1, class Other2
>
struct ContainerTraits< Container<Key,T,Other1,Other2> >{
    typedef Container<Key,T,Other1,Other2> ContainerT;
    // and the map returns pair<const Key,T> from the begin() function
    typedef typename ContainerT::value_type value_type;
};

Теперь, когда подготовка завершена, перейдем к функции get_front!

template<class Container>
typename ContainerTraits<Container>::value_type
get_front(Container const& c){
    // begin() is the only shared access function
    // to the first element for all standard container (except std::bitset)
    return *c.begin(); 
}

Уф!И это все!Полный пример можно увидеть на Ideone .Конечно, можно было бы уточнить это еще дальше, вплоть до возврата фактического значения, сопоставленного с ключом в std::map, или использовать функции доступа, специфичные для контейнера, но мне было немного лень это делать,: P


Редактировать
A way Более простой способ C ++ 0x - использование нового синтаксиса функции конечного возврата, из которогопример можно найти здесь на Ideone .


Edit 2
Ну, я не знаю почему, но я совершенно не думалвложенных typedef с при написании этого ответа.Я оставлю подробный путь в качестве справочника для классов признаков / паттернов, соответствующих шаблону. Это способ сделать это, в основном то же самое, что я делал с классами черт, но в конечном итоге менее многословно.

3 голосов
/ 26 марта 2011

Я предполагаю, что вы хотите оба Collection и T в качестве параметров шаблона.Для этого просто наберите

template< template < typename > class Collection, typename T >
T get_front( Collection< T > const& c )
...

Конструкция template < typename > class Collection сообщает компилятору, что Collection сам шаблон с одним параметром.

Редактировать : как указаноout be Xeo , vector принимает два параметра шаблона, и ваши шаблоны должны отражать это, то есть

template< template < typename, typename > class Collection, 
          typename T, typename Alloc >
T get_front( Collection< T, Alloc > const& c )
...
2 голосов
/ 26 марта 2011

Вы можете сделать это, если знаете, что это не ассоциативный контейнер.

template <typename Collection>
Collection::type_name get_front(Collection const& c)
{
  return c.front();
}
0 голосов
/ 26 марта 2011

Поскольку Collection<T> известно заранее, я думаю, что вы хотите:

template <typename T>
T get_front(Collection<T> const& c)
{
    return c.front();
}

Единственное, что меняется, это то, что T, это всегда в Collection (содержимое, а не контейнер), поэтому вы не поместили это как часть шаблона.

Если контейнер менялся, использование c.front() может быть опасным.Вам необходимо убедиться, что у типа коллекции есть метод front, который не принимает параметров, и вернуть T.

Редактировать:

Если вам нужнок шаблону Collection, то это больше похоже на:

template<typename C, typename T>
T get_front(C<T> const & c)

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

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