Давайте предположим, что я хочу сделать шаблонную функцию, которая возвращает первый элемент любого stl контейнера. Общий путь был бы:
template<typename Container>
Container::value_type first(Container c){
return *(c.begin());
}
Это работает на векторы, списки, двухсторонние очереди, наборы и так далее.
Однако для парных ассоциативных контейнеров (станд.:: карта), если хотел бы иметь
return c.begin()->second;
Как я мог протестировать (в функции или с шаблонной специализацией), если у меня есть парный ассоциативный контейнер?
Контейнер STL, кажется, не имеет никаких черт, присоединенных к нему. Это возможный проверить, имеет ли это a:: key_type?
Вы можете сделать это довольно легко:
namespace result_of // pillaged from Boost ;)
{
template <class Value>
struct extract { typedef Value type; };
template <class First, class Second>
struct extract < std::pair<First,Second> > { typedef Second type; };
}
template <class Value>
Value extract(Value v) { return v; }
template <class First, class Second>
Second extract(std::pair<First,Second> pair) { return pair.second; }
template <class Container>
typename result_of::extract< typename Container::value_type >::type
first(const Container& c) { return extract(*c.begin()); }
Я должен отметить, что, вероятно, я бы добавил тест, чтобы проверить, пуст ли контейнер
... Потому что, если контейнер пуст
, вы за неопределенное поведение.
В движении:
int main(int argc, char* argv[])
{
std::vector<int> vec(1, 42);
std::map<int,int> m; m[0] = 43;
std::cout << first(vec) << " " << first(m) << std::endl;
}
// outputs
// 42 43
Пример бессовестно взят из litb
;)
Этот работает:
template<typename T>
struct tovoid {
typedef void type;
};
template<typename T, typename = void>
struct value_type {
typedef typename T::value_type type;
static type get(T const& t) {
return *t.begin();
}
};
template<typename T>
struct value_type<T, typename tovoid<typename T::mapped_type>::type> {
typedef typename T::mapped_type type;
static type get(T const& t) {
return t.begin()->second;
}
};
template<typename Container>
typename value_type<Container>::type first(Container const& c){
return value_type<Container>::get(c);
}
int main() {
std::map<int, int> m; m[0] = 42; std::cout << first(m);
std::vector<int> a(1, 43); std::cout << first(a);
}
(выводит 4243
)
Использовать специализацию функции шаблона:
template<typename Container>
typename Container::value_type first(typename Container c)
{
return *(c.begin());
}
template<typename K, typename V>
typename V first(std::map<K,V> & c)
{
return c.begin()->second;
}