2011-06-25 19 views
10

Chcę zbudować klasę Stack, aby użytkownik mógł wybrać kontener, który chce wykorzystać do implementacji Stack. Na przykład List/Vector.Szablon szablonów C++ (podwójny szablon?)

kod Częściowa:

stack.h

#ifndef STACK_H_ 
#define STACK_H_ 

template <typename T, template<typename T> class ContainerType> 
class Stack{ 
    ContainerType<T> container; 
public: 
    Stack() : container(ContainerType<T>()){} 

}; 

#endif /* STACK_H_ */ 

test.cpp

#include "stack.h" 
#include <vector> 

int main(){ 
    Stack<int, std::vector<int> > stack; 
    return 0; 
} 

Cóż, to nie kompiluje. Mam kolejne błędy na linii:

Stack<int, std::vector<int> > stack; 

błędów:

expected a class template, got `std::vector<int, std::allocator<int> >' test.cpp 

invalid type in declaration before ';' token test.cpp 

type/value mismatch at argument 2 in template parameter 
list for `template<class T, template<class T> class ContainerType> 
class Stack' test.cpp 

‪ 
+3

wektor jest w std namespace – nullpotent

+0

Ten błąd jest konsekwencją kulawej praktyki polegającej na tym, że zawsze piszę 'using namespace std;' i przyzwyczajam się do niego. –

+0

@Armen Ale prawdziwy błąd jest gdzie indziej. –

Odpowiedz

22

Pierwszy byłoby std::vector i nic więcej, bo vector rezyduje w przestrzeni nazw std i prosicie o szablonu szablonu parametr, natomiast std::vector<int> nie jest już szablon. Następnie std::vector faktycznie ma dwa parametrów szablonu, jeden dla danego typu, a inne dla podzielnika:

template < 
    typename T, 
    template<typename, typename> class ContainerType, 
    typename Alloc = std::allocator<T> 
> 
class Stack{ 
    ContainerType<T, Alloc> container; 
    // ... 
}; 

// usage: 
Stack<int, std::vector> s; 

Teraz ta umożliwia jedynie pojemniki z dwoma parametrami szablonu takiego typu bazowego, tak jesteś lepszy off z tym, co standardowy sposób: wziąć go jako normalnego typu:

template <typename T, typename ContainerType> 
class Stack{ 
    ContainerType container; 
    // ... 
}; 

// usage: 
Stack<int, std::vector<int> > s; 

Aby upewnić się, że typ bazowy ma taką samą T można zrobić fake „statyczny assert”, lub jeśli masz C++ 0x włączony kompilator, możesz zrobić faktyczny statyczny assert:

#include <tr1/type_traits> // C++03 us std::tr1::is_same 
//#include <type_traits> // C++0x, use std::is_same 

template <typename T, typename ContainerType> 
class Stack{ 
    typedef typename ContainerType::value_type underlying_value_type; 
    typedef char ERROR_different_value_type[ 
       std::tr1::is_same<T, underlying_value_type>::value ? 1 : -1 
             ] 
    ContainerType container; 
    // ... 
}; 

To działa, ponieważ jeśli T różni się od używanych kontenera T, będzie to typedef char ERROR_different_vale_type[-1] i tablicą z negatywnej wielkości nie mogą ewentualnie występować, co powoduje błąd kompilatora. :) Teraz, z C++ 0x można po prostu static_assert że:

#include <tr1/type_traits> // C++03 
//#include <type_traits> // C++0x 

template <typename T, typename ContainerType> 
class Stack{ 
    typedef typename ContainerType::value_type underlying_value_type; 
    static_assert(std::tr1::is_same<T, underlying_value_type>::value, 
    "Error: The type of the stack must be the same as the type of the container"); 
    ContainerType container; 
    // ... 
}; 

Dla wygody, można teraz określić argument domyślny szablon dla wspólnej sprawy:

template <typename T, typename ContainerType = std::vector<T>> 
class Stack{ 
    ContainerType container; 
    // ... 
}; 

// usage: 
Stack<int> s; 

I w tym momencie może po prostu użyć std::stack, który robi dokładnie to (chociaż używa std::deque jako typ bazowy).:)

+0

Tak, ale chcę również móc napisać Stack > stos, a nie tylko wektor. Jeśli użyję twojego drugiego bloku kodu, co się stanie, jeśli użyję: Stack > stack. Czy będzie to tylko podwójny wektor? Czy mogę utworzyć ograniczenie, aby wektor był typu int, a nie podwójny (mam na myśli, że będzie jak pierwszy typ)? – user550413

+0

@ user550413: jeśli napiszesz 'Stack ', wówczas typem kontenera będzie 'List ', tak samo jak gdy 'Stack ' dostaje 'wektor'. – vines

+0

@user: W przypadku drugiej wersji zobacz moją edycję. – Xeo

1

Linia:

 Stack<int, vector<int> > stack; 

powinno być:

Stack<int, std::vector<int> > stack; 

czy można poprzedzić test.cpp z

using namespace std; 
+0

Po co wprowadzać całą przestrzeń nazw, gdy potrzebuje tylko 'using std :: vector'? –

+0

Edytowane, nowe błędy powyżej. – user550413

+0

@ user550413 spójrz na inne odpowiedzi. –

2

Jako, że vector należy do przestrzeni nazw std, musisz ją zakwalifikować. Ale poza tym, jak ContainerType jest parametrem szablonu szablon trzeba przekazać szablon, a nie ostateczny typ:

Stack<int, std::vector > stack; 
2

Najprostszym sposobem nie jest w użyciu szablonu parametr szablonu, z powodu problemu z liczbę operandów pojemniki.

Zamiast tego wystarczy podać pełny typ kontenera i tylko to. Następnie wyodrębnij value_type (standardowy STL wewnętrzny typedef), aby uzyskać wartość.

template <typename Container> 
class Stack 
{ 
public: 
    typedef typename Container::value_type value_type; 

private: 
    Container _container; 
}; // class Stack<Container> 

Następnie można po prostu użyć go jako Stack< std::vector<int> > i będzie ona zawierać int s.