Как сделать независимым контейнер аргумента функции - PullRequest
5 голосов
/ 27 марта 2011

Я пишу служебную функцию, которая будет принимать вектор элементов (может быть string, int, double, char), объединять в одну строку и возвращать ее. Это выглядит так:

template<typename T>
std::string convert2Str(std::vector<T> const& vec) 
{
   std::ostringstream sStream; 
   for (size_t k=0; k<vec.size(); ++k) {
      sStream << vec[k] << " "; 
   }
   return sStream.str(); 
}

Я бы хотел сделать эту функцию более общей:

  • Сначала используйте итераторы вместо индексов для vector<T>. Я пробовал это std::vector<T>::const_iterator it = vec.begin() перед циклом и компилятор выдал ошибку: : ошибка: ожидается ; до этого Когда я изменяю вышеприведенные определения на std::vector<std::string>::const_iterator it = vec.begin(), ошибка исчезает. Итак, похоже, что я не следую правильному синтаксису, пожалуйста, дайте мне знать, что это такое
  • Второе - сделать функцию более общей, сделав контейнер первого аргумента независимым. Для любого контейнера (vector, list, queue, deque и т. Д.) Я хочу сделать то же самое, что и выше. Я попытался найти это в stackoverflow и не нашел удовлетворительного ответа.

Ответы [ 5 ]

5 голосов
/ 27 марта 2011

Следуя практике STL, я бы рекомендовал использовать два итератора для входных параметров вместо контейнера (по очевидной причине возможности работать только с частью контейнера и, как правило, с любой последовательностью, определенной итераторами):

template<typename InputIterator>
std::string convert2Str(InputIterator first, InputIterator last)
{
    std::ostringstream sStream;
    for (InputIterator it = first; it != last; ++it) {
       sStream << *it << " ";
    }
    return sStream.str();
}

Если вам нужен тип содержащихся объектов, используйте

typedef typename std::iterator_traits<InputIterator>::value_type T;

ДОБАВЛЕНО: Затем вы можете использовать функцию следующим образом:

std::vector<int> int_vec;
std::list<float> f_list;
std::deque<std::string> str_deq;

     // put something into the containers here

std::cout<< convert2Str(int_vec.begin(), int_vec.end()) <<std::endl;
std::cout<< convert2Str(f_list.begin(), f_list.end()) <<std::endl;
std::cout<< convert2Str(str_deq.begin(), str_deq.end()) <<std::endl;

Обратите внимание,не может перебрать std :: queue;но если вам это действительно нужно, стандарт гарантирует достаточную поддержку для самостоятельного решения.См. Дополнительную информацию здесь: std :: queue iteration .

5 голосов
/ 27 марта 2011

Шаг 1, как вы сказали, используйте итераторы:

template<typename T>
std::string convert2Str(std::vector<T> const& vec) 
{
   typedef std::vector<T> container;
   std::ostringstream sStream; 
   for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

Шаг 2, сделайте аргумент шаблона типом контейнера вместо типа элемента (вы можете получить тип элемента обратно с помощью value_type:

template<typename container>
std::string convert2Str(container const& vec)
{
   typedef container::value_type T; // if needed
   std::ostringstream sStream; 
   for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

В C ++ 0x это становится еще проще (а typename не требуется):

template<typename container>
std::string convert2Str(container const& vec)
{
   using std::begin;
   using std::end;
   std::ostringstream sStream;
   for (auto it = begin(vec); it != end(vec); ++it) {
      typedef decltype(*it) T; // if needed
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

Среди прочих преимуществ std::begin и std::end работают для необработанных массивов.

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

Проще всего, если вы используете шаблоны только для типа контейнера; тип значения хранится во всех стандартных контейнерах, Boost и Qt как typedef member value_type. std::copy и ostream_iterator позволяют пропускать длинные итераторские объявления.

template <typename Container>
std::string convert2Str(Container const &cont)
{
    std::ostringstream s;
    std::copy(cont.begin(), cont.end(),
              std::ostream_iterator<typename Container::value_type>(s, " "));
    return s.str();
}

typename необходимо, чтобы избежать двусмысленности. Последние версии GCC будут предупреждать вас, когда вы пропустите это ключевое слово.

2 голосов
/ 27 марта 2011

Используйте это. Вам нужна часть typename, чтобы сообщить компилятору, что он должен учитывать T::const_iterator тип во время синтаксического анализа, он не может действительно знать, что это так, пока вы на самом деле не вызовете функцию, передающую некоторую T, которая имеет const_iterator тип члена.

template<typename T>
std::string convert2Str(T const& cont) 
{
    std::ostringstream sStream; 
    for (typename T::const_iterator it = cont.begin(); it != cont.end(); ++it) {
        sStream << *it << " "; 
    }
    return sStream.str(); 
}
0 голосов
/ 27 марта 2011

Я думаю, что это должно работать:

template<typename T>
std::string convert2Str(T const& container) 
{
   std::ostringstream sStream; 
   for (typename T::const_iterator i= container.begin(); i != container.end(); ++i) {
      sStream << *i << " "; 
   }
   return sStream.str(); 
}

Демо: http://ideone.com/9pUVV

...