Шаблоны C ++ (двойной шаблон?) - PullRequest
11 голосов
/ 25 июня 2011

Я хочу создать класс 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

‪

Ответы [ 4 ]

22 голосов
/ 25 июня 2011

Во-первых, это будет 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, вы можете сделать поддельное «статическое утверждение» или, если у вас есть компилятор с поддержкой C ++ 0x, вы можете сделать фактическое статическое утверждение:

#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], и массив отрицательного размера не может существовать, что вызывает ошибку компилятора. :) Теперь с C ++ 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 в качестве базового типа). :)

3 голосов
/ 25 июня 2011

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

Вместо этого просто передайте полный тип контейнера, и только это. Затем извлеките 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 с.

2 голосов
/ 25 июня 2011

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

Stack<int, std::vector > stack;
1 голос
/ 25 июня 2011

Эта строка:

     Stack<int, vector<int> > stack;

должна быть:

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

или вы можете добавить test.cpp к

 using namespace std;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...