2011-01-11 4 views
1

Я хотел бы написать функцию, которая будет принимать контейнер stl (как набор, вектор или список) , а затем перебирать содержимое и затем добавлять их к и верните строку.Как templatize функция на контейнере stl, который имеет итератор

Нечто подобное.

// I dont know how to do this. Just using stl::container for meanings sake Not sure if such a thing exists? 
template<typename T, typename Container = stl::container<T> > 
void JoinToString(const Container<T> cont, const char * delim, string &str) 
{ 
    stringstream s; 
    Container<T>::const_iterator it = cont.begin, last = cont.end(); 
    while(it != last) 
    { 
     s<<(*it); 
     ++it; 
     if(it == last) 
      break; 
     s<<delim; 
    } 
    str = s.str(); 
} 

Я хочу что-то в этом роде. Не знаете, как написать такой код.

ответ

1

Вам нужно решить, что вы хотите. Вы можете передать тип или шаблон. Не оба. В опубликованном вами коде вы объявляете Container типу be, но используете его в качестве шаблона.

template<typename T, typename Container = vector<T> > 
void test() { Container x; }; 

template<typename T, template <typename> class Container = vector > 
void test() { Container<T> x; } 
+0

Прохождение шаблона не является тривиальным. Для начала ваш код не будет работать во всех стандартных компиляторах, так как контейнер 'std :: vector' принимает как минимум два аргумента типа. Тогда проблема в том, что стандарт позволяет реализациям добавлять дополнительные параметры до тех пор, пока они дефолтны, то есть в идеально соответствующей реализации, 'std :: vector' может иметь 3, 4, 5 ... аргументы. –

+0

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

0

Ваше решение почти правильно. Просто сделайте это:

template<typename Container > 
string JoinToString(const Container & cont, const string &delim) 
{ 
    stringstream s; 
    for (Container::const_iterator it = cont.begin(); it != cont.end(); it++) 
    { 
      s<<(*it); 
      if ((it+1) != cont.end()) 
        s<<delim; 
    } 
    return s.str(); 
} 

Лучше функция будет так:

template<typename FwdIt> 
string JoinToString(FwdIt from, FwdIt to, const string &delim) 
{ 
    stringstream s; 
    for (; from != to; from++) 
    { 
     s<<(*from); 
     if ((from+1) != to) 
      s<<delim; 
    } 
    return s.str(); 
} 

Используя это решить from и to с помощью которых соединить элементы!

+0

Пробовал первое решение, но не компилируется. Я использую Linux. Он говорит, что ожидается ';' перед этим и «это» не было объявлено в этой области – AMM

+0

@AMM: Я думаю, код, который вы пытаетесь скомпилировать, не совсем то же, что я написал здесь. пожалуйста, проверьте его, если он такой же, строка за строкой, точка с запятой в пол-двоеточия! – Nawaz

7

Стиль STL должен передавать begin и итераторы любого алгоритма, а не самого контейнера: это сохраняет общие вещи и позволяет использовать нативные векторы с указателями. Общие соображения стиля C++ также предполагают возвращение std::string вместо использования ссылочного параметра.

1

Если вам действительно нужен доступ к контейнеру, то это будет делать то, что вы хотите:

template<typename Container> 
void JoinToString(const Container& cont, const char * delim, string &str) 
{ 
    typedef typename Container::value_type T; 
    ... 
} 

Однако, это более идиоматических использовать диапазон итератора, как это:

template<typename FwdIt> 
void JoinToString(FwdIt it, FwdIt end, const char * delim, string &str) 
{ 
    typedef typename std::iterator_traits<Container::iterator>::value_type T; 
    while(it != end) 
    { 
    ... 
    } 
} 
+0

+1 для использования 'value_type' и использования диапазона итераторов. –

0

Это рабочий пример,

template<typename T> 
std::string JoinToString(const T& cont, const char* delim, std::string &str) 
{ 
    std::stringstream s; 
    T::const_iterator it= cont.begin(); 
    T::const_iterator last= cont.end(); 
    while(it != last) 
    { 
     s << (*it); 
     ++it; 
     s << delim; 
     if (it == last) 
     break; 
    } 
    return s.str() + str; 
} 

int main() 
{ 
    std::string s("String! "); 
    std::vector<std::string> v(1, "String!, String!"); 
    std::cout << JoinToString(v, ", ", s) << "\n"; 

    std::list<std::string> l(1, "String!, String!"); 
    std::cout << JoinToString(l, ", ", s); 
} 

Следует отметить, что стоит отметить несколько вещей. Вы можете использовать template<template<class> class T, хотя это может вызвать проблемы, в зависимости от количества аргументов шаблона, которые имеет контейнер.

Я хотел бы отметить (для дальнейшего использования), если вы хотите подключить тип к шаблону класса, например. СТАНД :: строка в качестве аргумента шаблона в станд :: вектор самое безопасное решение,

template<class T> 
struct something 
{ 
    typedef typename boost::mpl::apply<T, std::string>::type type; 
}; 
something<std::vector<boost::mpl::placeholders::_1>>::type; 

Причиной этого является более безопасным, чем использование template<template<class> class T, является то, что это позволит более точно настраивать со стороны пользователя и будет работать над шаблонами классов с любым количеством аргументов/аргументов по умолчанию.

1

Еще одно решение, которое делает именно то, что вы хотите boost::algorithm::join:

Этот алгоритм объединяет все строки в списке «» в одну длинную строку. Сегменты объединены данным разделителем.

Пример использования:

#include <boost/algorithm/string/join.hpp> 
#include <boost/assign/list_of.hpp> 
#include <iostream> 
#include <string> 
#include <vector> 

int main() 
{ 
    std::vector<std::string> v = boost::assign::list_of("A")("B")("C"); 
    std::cout << boost::algorithm::join(v, "/") << std::endl; 
} 

Выходы: A/B/C

0

Создание пользовательского вывода итератора:

struct append_to_string_with_delim 
    : std::iterator<std::output_iterator_tag, void, void, void, void> 
{ 
    append_to_string_with_delim(std::string &ss, char const *dd) : s(ss), d(dd) 
    { 
    } 
    template<typename T> 
    append_to_string_with_delim &operator=(T const &t) 
    { 
    std::ostringstream o; 
    o << t; 
    s += o.str(); 
    s += d; 
    return(*this); 
    } 
    append_to_string_with_delim &operator*() 
    { 
    return(*this); 
    } 
    append_to_string_with_delim &operator++() 
    { 
    return(*this); 
    } 
    append_to_string_with_delim const &operator++(int) 
    { 
    return(*this); 
    } 
    std::string &s; 
    char const *const d; 
}; 

и использовать зЬй :: копию:

std::vector<int> v; 
std::string s("The v vector elements are: "); 
... 
copy(v.begin(), v.end(), append_to_string_with_delim(s, " ")); 
+0

В конце std :: copy добавляется разделитель. – Christian

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