Шаблон шаблонов С++ (двойной шаблон?)

Я хочу создать класс Stack, чтобы пользователь мог выбрать, какой контейнер он хочет использовать для реализации Stack. Например, List/Vector.

Частичный код:

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;
}

Ну, он не компилируется. Я получаю следующие ошибки в строке:

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

Ошибка:

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

‪

Ответ 1

Во-первых, это будет std::vector, и ничего больше, потому что vector находится в пространстве имен std, и вы запрашиваете параметр шаблона шаблона, а std::vector<int> - не шаблон больше. Затем std::vector фактически принимает параметры два, один для типа и другой для распределителя:

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;

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

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

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

Чтобы гарантировать, что базовый тип имеет тот же T, вы можете сделать поддельное "static assert", или если у вас есть компилятор с поддержкой С++ 0x, вы можете сделать фактическое static 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;
  // ...
};

Это работает, потому что если T отличается от используемого контейнера T, это будет typedef char ERROR_different_vale_type[-1], и массив отрицательного размера не может существовать, что вызывает ошибку компилятора.:) Теперь, с С++ 0x, вы можете просто static_assert что:

#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;
  // ...
};

Для удобства теперь вы можете указать аргумент шаблона по умолчанию для общего случая:

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

// usage:
Stack<int> s;

И на этом этапе вы можете просто использовать std::stack, который выполняет именно это (хотя в качестве базового типа он использует std::deque).:)

Ответ 2

Поскольку vector принадлежит пространству имен std, вы должны его квалифицировать. Но кроме этого, поскольку ContainerType является параметром шаблона шаблона, вам необходимо передать шаблон, а не конечный тип:

Stack<int, std::vector > stack;

Ответ 3

Самый простой способ - не использовать параметр шаблона шаблона из-за проблемы с ясностью контейнеров.

Вместо этого просто передайте полный тип контейнера и только это. Затем извлеките value_type (стандартный STL внутренний typedef), чтобы получить значение.

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

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

Затем вы можете просто использовать его как Stack< std::vector<int> >, и он будет содержать int s.

Ответ 4

Эта строка:

     Stack<int, vector<int> > stack;

должен быть:

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

или вы можете префикс test.cpp с помощью

 using namespace std;