2013-11-12 3 views
4

В своем лейтмотивом этого года Going Native The Essence of C++ (переход к 40:30) Бьерн Страуструп приводит следующий пример кода:VALUE_TYPE параметра шаблона контейнера

template<typename C, typename V> 
vector<Value_type<C>*> find_all(C& cont, V v) 
{ 
    vector<Value_type<C>*> res; 

    for (auto& x : cont) 
     if (x == v) 
      res.push_back(&x) 

    return res; 
} 

Эта функция используется, чтобы найти все вхождения значение в контейнере и возвращает указатели на найденные элементы. Пример с видео:

string m{"Mary had a little lamb"}; 
for (const auto p: find_all(m,'a')) // p is a char* 
    if (*p != 'a') 
     cerr << "string bug!\n"; 

Мой вопрос о Value_Type<C>*. Есть ли что-то подобное в стандартной библиотеке ? Я искал его и не нашел. Как это можно реализовать, если это не в std?

+1

Какая цель у вас здесь? Что делает 'Value_type'? Вопрос должен быть самодостаточным и не требовать от нас смотреть 96-минутное видео. – interjay

+0

@interjay Вам просто нужно посмотреть первые 42 минуты или так :-) Но вы правы. Я отредактирую вопрос. – guini

ответ

8

Я не знаю, это в стандарте, но это не сложно реализовать:

template <class C> 
    struct value_type 
    { 
     typedef typename C::value_type type; 
    }; 

    template <class T, int N> 
    struct value_type<T[N]> 
    { 
     typedef T type; 
    }; 

    template <class T> 
    struct value_type<T*> 
    { 
     typedef T type; 
    }; 

и теперь вы можете использовать typename value_type<C>::type для доступа к типу, который содержит контейнер. Если у вас есть собственный контейнер, который вы хотели бы использовать, но у него нет value_type typedef (и по какой-либо причине вы не можете его изменить), вы можете просто специализировать эту структуру для этого контейнера.

Чтобы избежать typename ...::type вы можете сделать:

template <class C> 
    using Value_Type = typedef value_type<C>::type; 

и теперь вы просто использовать Value_Type<C> везде.

EDIT
Стефан предложил в ответ в ближайшее время, вы можете сделать это легко с std::begin что хорошо, потому что любой контейнер вы используете/создать вы хотели бы быть в состоянии назвать std::begin и std::end в любом случае:

template <class C> 
    using Value_Type = typename std::remove_reference< 
     decltype(*std::begin(std::declval< 
      typename std::add_lvalue_reference<C>::type>()))>::type; 

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

+2

Немного ошибочно: добавьте 'typename std :: remove_reference <' ...'> :: type' вокруг вашего объекта decltype ;-) Демо: http://ideone.com/bCF0qz – stefan

+0

@stefan да, конечно – SirGuy

+1

[Не работает с массивами] (Http: // coliru.stacked-crooked.com/a/0a0582b2710b362b) – soon

3

Value_type<C> всего лишь typedef для C::value_type. Насколько, я знаю, нет таких ЬурейеЕ в стандартной библиотеке, но вы можете определить сами:

template <class T> 
using Value_type = typename T::value_type; 

template<typename C, typename V> 
std::vector<Value_type<C>*> find_all(C& cont, V v) 
{ 
    std::vector<Value_type<C>*> res; 

    for (auto& x : cont) 
     if (x == v) 
      res.push_back(&x); 

    return res; 
} 

int main() 
{ 
    std::vector<int> v{1, 2, 3, 3, 5}; 

    for(const auto x: find_all(v, 3)) 
    { 
     std::cout << *x << std::endl; 
    } 
} 

Но, как это было предложено @stefan, это будет работать только с стандартными контейнерами. Вы можете получить базовый тип с помощью std::begin функции (которая определена для массивов тоже), как это реализовано в @ GuyGreer отвечает

+1

Это правильно для стандартных контейнеров, но, например, не для массивов. Afaik, 'std :: begin' работает с любым типом диапазона. Вероятно, есть хороший способ получить базовый тип из итератора, возвращаемого из 'std :: begin'. – stefan

+0

Хороший улов, спасибо. Я собираюсь добавить это к ответу. – soon

Смежные вопросы