Допустим, у вас есть шаблон функции, которая принимает универсальный контейнер T
:
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
// Do something
}
В этом методе, вы хотите, чтобы получить итератор к чему-то в контейнере. Но вы не знаете тип итератора; вы только дали тип контейнера, а не итератора:
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
/* ??? */ it = input.begin();
}
Скажем, ради аргумента, что вы могли бы решить эту проблему, и вы хотели разыменования итератора. Чтобы сохранить результат, вам необходимо знать, что такое T
; но это вызывает та же проблема снова:
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
/* ??? */ it = input.begin();
/* ??? */ firstElement = *it;
}
Мы не знаем, что T
, тип, содержащийся в контейнере; у нас есть только тип самого контейнера в этом шаблоне функции. Чтобы получить тип внутри контейнера, нам нужен контейнер, чтобы немного помочь нам и рассказать нам, что это за его тип. Класс контейнера делает это через typedef
. Для стандартных контейнеров, это было бы iterator
для типа итератора и value_type
для типа содержащегося значения:
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
// Container provides its iterator type via a typedef.
typename ContainerT::iterator it = input.begin();
// Container provides its contained type via a typedef.
typename ContainerT::value_type firstElement = *it;
}
Даже в C++ 11, где можно было бы решить приведенный выше пример с auto
:
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
// Make the compiler figure it out:
auto it = input.begin();
auto firstElement = *it;
}
иногда вы все еще хотите, чтобы иметь возможность получить фактический тип для других целей:
// Failure to compile: http://ideone.com/vxJ2IU
// Successful compile: http://ideone.com/b5fU3S
#include <vector>
#include <list>
#include <deque>
#include <type_traits>
#include <iostream>
template <typename ContainerT>
void DoThings(ContainerT const& input)
{
// DoThings only accepts integral containers:
static_assert(
std::is_integral<typename ContainerT::value_type>::value,
"DoThings requires that the contained type be integral");
// Make the compiler figure it out:
auto it = input.begin();
auto firstElement = *it;
std::cout << firstElement;
}
int main()
{
std::vector<int> abc;
abc.push_back(42);
std::list<long> def;
def.push_back(1729);
std::deque<short> queue;
queue.push_back(1234);
DoThings(abc);
DoThings(def);
DoThings(queue);
// Does not compile due to static assert:
std::vector<double> doubles;
doubles.push_back(3.14);
DoThings(doubles);
}
+1, но для использования вами 'value_type' требуется' typename', потому что они являются зависимыми от шаблона именами типов. (Пример без имени типа: http://ideone.com/JVBlBy; пример с: http://ideone.com/EbCbr1) –
@BillyONeal о да. Спасибо. – stardust
(Erm мои примеры сосут, потому что я не инициализировал общее количество, ну хорошо) –