2014-12-06 3 views
3

Как определить шаблон шаблона в контейнере и типе?шаблоны классов в качестве параметров шаблона

Например, оператор вставки перегрузки, чтобы передавать все элементы вектора, списка или, вперед итератора контейнера:

using namespace std; 

#include <iostream> 
#include <vector> 
#include <list> 


//... 
//...the second argument is a container template-ed on type T 
//... 
template <typename T,template <typename U> class C> 
ostream& 
operator<< 
    (ostream& p_os,const C<T>& p_c) 
{ 
    for(typename C<typename T>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit) 
    { 
    p_os.operator<<(*cit); 
    } 
    return p_os; 
} 

int 
main 
() 
{ 
    vector<int> v; 
    cout << v << endl; 
    list<int> l; 
    cout << l << endl; 
    return 0; 
} 

Это не компилируется на г ++ 4.9. Что не так? Как это делается?

ответ

1

Почему бы просто не передать тип контейнера в качестве параметра шаблона и узнать из него тип элемента? В вашем примере коде не нужно даже тип элемента:

template <typename C> 
ostream& 
operator<< 
    (ostream& p_os,const C& p_c) 
{ 
    typedef typename C::value_type element_type; // if needed 
    for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit) 
    { 
    p_os.operator<<(*cit); 
    } 
    return p_os; 
} 

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

EDIT: Вы могли бы, например, попытка ограничить это классы с вложенной value_type (который все контейнеры имеют):

template <typename C, typename T = typename C::value_type> 
ostream& 
operator<< 
    (ostream& p_os,const C& p_c) 
+0

Этот подход делает то, что я хотел. Я продлил это, чтобы поддерживать потоки всех типов контейнеров – silvermangb

+0

"(Хотя это может быть неразумно использовать это для глобальных функций, подобных этому, без какой-либо интриги enable_if, поскольку в противном случае он будет соответствовать любому аргументу.)« Я попытался использовать запятую для разделения записи контейнера, но компилятор попытался использовать эту функцию для потоковой передачи строки. Как избежать этого с помощью enable_if? – silvermangb

+0

См. Edit (который фактически не использует 'enable_if'). Для более сложного и мощного решения см. Http://stackoverflow.com/questions/9242209/is-container-trait-fails-on-stdset-sfinae-issue. –

1

std::vector шаблон класса, который имеет два параметров типа шаблона:

template <class T, class Alloc = allocator<T> > 
class vector; 

Чтобы сделать вашу функцию работы с std::vector (и других шаблонов классов два-параметра), вы можете использовать следующее определение:

template <typename T, typename A, template <typename, typename> class C> 
//     ~~~~~~~~~^      ~~~~~~~^ 
ostream& operator<<(ostream& p_os, const C<T,A>& p_c) 
//           ^^ 
{ 
    for(typename C<T,A>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit) 
    { 
    p_os.operator<<(*cit); 
    } 
    return p_os; 
} 

или в качестве альтернативы:

template <typename T, template <typename...> class C> 
ostream& operator<<(ostream& p_os, const C<T>& p_c); 
+0

'vector' имеет * как минимум * два параметра шаблона - реализация может добавить больше, если у них есть значения по умолчанию. Поэтому первая версия не переносима. –

+0

@AlanStokes Я вам не верю, где стандарт так говорит? –

+0

Мои извинения, эта часть «общего знания» оказывается неправдой. Было предложено, но отклонено: http://stackoverflow.com/questions/1469743/standard-library-containers-with-additional-optional-template-parameters –

0

Алан Стокс подход работает. Код ниже может передавать любой контейнер. Мне просто пришлось добавить оператора ввода для карт

using namespace std; 

#include <iostream> 

#include <vector> 
#include <list> 
#include <forward_list> 
#include <set> 
#include <deque> 
#include <array> 
#include <map> 
#include <unordered_map> 

//... 
//...needed for map types which are (key,value) pairs. 
//... 
template <typename K,typename V> 
ostream& 
operator<< 
    (ostream& p_os,const pair<const K,V>& p_v) 
{ 
    std::operator<<(p_os,'('); 
    p_os << p_v.first; 
    std::operator<<(p_os,','); 
    p_os << p_v.second; 
    std::operator<<(p_os,')'); 
    return p_os; 
} 

template <typename C, typename T = typename C::iterator> 
ostream& 
operator<< 
    (ostream& p_os,const C& p_c) 
{ 
    for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit) 
    { 
    typename C::value_type v = *cit; 
    p_os << v; 
    std::operator<<(p_os,","); 
    } 
    return p_os; 
} 

int 
main 
() 
{ 
    vector<int> v; 
    for(int i=0;i<4;++i) 
    { 
    v.push_back(i); 
    } 
    cout << v << endl; 
    list<int> l; 
    for(int i=0;i<4;++i) 
    { 
    l.push_back(i); 
    } 
    cout << l << endl; 
    forward_list<int> fl = {0,1,2,3}; 
    cout << fl << endl; 
    set<int> s; 
    for(int i=0;i<4;++i) 
    { 
    s.insert(i); 
    } 
    cout << s << endl; 
    deque<int> d; 
    for(int i=0;i<4;++i) 
    { 
    d.push_back(i); 
    } 
    cout << d << endl; 
    array<int,4> a = {0,1,2,3}; 
    cout << a << endl; 
    unordered_map<int,int> um; 
    for(int i=0;i<4;++i) 
    { 
    um[i] = i; 
    } 
    cout << um << endl; 
    map<int,int> m; 
    for(int i=0;i<4;++i) 
    { 
    m[i] = i; 
    } 
    cout << m << endl; 
    return 0; 
} 
+0

Все еще не совсем правильно. Вызывает неоднозначность для оператора << для std :: string. – silvermangb

+0

Ну, 'string' - это контейнер, поэтому он должен соответствовать ему. –

+0

Тем не менее, я хотел бы обходной путь для строки. Сериализация любого оператора контейнера << без предотвращения переноса строк или других объектов - это то, что я хотел в течение длительного времени. – silvermangb