Исключение типа из шаблона класса - PullRequest
0 голосов
/ 19 января 2019

У меня сейчас проблемы с stack, когда я испортил один из моих шаблонных классов.
Проблема в том, что такие методы, как pusk_back, begin или end, не могут использоваться с этим типом.
Поэтому моя цель здесь состоит в том, чтобы предотвратить создание экземпляра содержимого с помощью stack<T> или добавить условие, предотвращающее попадание экземпляра stack в эти части кода.
Вот как выглядит class:

template<typename T, template <typename...> typename U>
class contain {
    public:
        contain() : _container()
        {
        }
        ~contain()
        {
        }
        void push(T const&data)
        {
            this->_container.push_back(data);
        }
        void aff()
        {
            std::for_each(_container.begin(), _container.end(),
            [](T &var) {::aff(var);});
        }
        void add()
        {
            std::for_each(_container.begin(), _container.end(),
            [](T &var) {::add(var);});
        }
    private:
        U<T> _container;
};

Я пытался специализировать шаблон, но не смог найти синтаксис и не смог проверить тип переменной во время выполнения.

1 Ответ

0 голосов
/ 19 января 2019

Я должен признать, что template все еще кажутся мне чёрной магией.Я успешно использовал специализации шаблона в различных ситуациях (если у меня не было лучшей идеи), но никогда не использовал аргументы шаблона.Итак, я попытался ...

Когда я попробовал Coliru (компилятор g++ (GCC) 8.1.0), я начал с -std=c++11 и вскоре получил несколько ужасных ошибок.К счастью, был также совет перейти на -std=c++17.Я сделал, и вещи становятся намного лучше сразу.Я грубо вспомнил, что аргументы шаблона не очень хорошо поддерживаются в C ++ 11.Короткий взгляд на cppreference подтвердил мою правоту:

Параметр шаблона шаблона

template <параметр-список> typename (C++ 17) |имя класса (необязательно) (1)шаблон <список параметров> имя типа (C ++ 17) |имя класса (необязательно) = по умолчанию (2)шаблон <список параметров> имя типа (C ++ 17) |класс ... имя (необязательно) (3) (начиная с C ++ 11)

1) Параметр шаблона шаблона с необязательным именем.2) Параметр шаблона шаблона с необязательным именем и значением по умолчанию.3) Пакет параметров шаблона шаблона с необязательным именем.

Получив это пояснение, я взял часть примера кода OP и добавил частичную специализацию для template, где I

  • оставил первый параметр
  • специализированный второй параметр с std::stack.

На самом деле, это не сильно отличается от того, как специализируются шаблоны с аргументами типа или значения.

cppreference имеет статью дляэто частичная специализация шаблона , но при использовании этого термина в качестве ключевого слова для поиска должно быть много обращений к книгам и учебным пособиям.(Я помню книгу, в которой я когда-то учил шаблоны C ++, тоже упомянул об этом -).

Итак, вот мой небольшой пример, чтобы продемонстрировать это:

#include <iostream>
#include <stack>
#include <vector>

template<typename T, template <typename...> typename U>
class ContainerT {
  private:
    U<T> _container;
  public:
    ContainerT(): _container() { }
    ~ContainerT() = default;
    ContainerT(const ContainerT&) = default;
    ContainerT& operator=(const ContainerT&) = default;

    bool empty() const { return _container.empty(); }
    void push(const T &data) { _container.push_back(data); }
    T pop()
    {
      const T data = _container.back();
      _container.pop_back();
      return data;
    }
};

template<typename T>
class ContainerT<T, std::stack> {
  private:
    std::stack<T> _container;
  public:
    ContainerT(): _container() { }
    ~ContainerT() = default;
    ContainerT(const ContainerT&) = default;
    ContainerT& operator=(const ContainerT&) = default;

    bool empty() const { return _container.empty(); }
    void push(const T &data) { _container.push(data); }
    T pop()
    {
      const T data = _container.top();
      _container.pop();
      return data;
    }
};

#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  // for std::vector
  DEBUG(ContainerT<int, std::vector> vec);
  DEBUG(vec.push(1); vec.push(2); vec.push(3));
  DEBUG(while (!vec.empty()) std::cout << vec.pop() << '\n');
  // for std::stack
  DEBUG(ContainerT<int, std::stack> stk);
  DEBUG(stk.push(1); stk.push(2); stk.push(3));
  DEBUG(while (!stk.empty()) std::cout << stk.pop() << '\n');
  // done
  return 0;
}

Очевидно, что ContainerTустанавливается для ContainerT<int, std::vector>.В противном случае метод push() не может быть скомпилирован.

Для ContainerT<int, std::stack> вместо этого используется специализация.(Опять же, в противном случае метод push() не может быть скомпилирован.)

Вывод:

ContainerT<int, std::vector> vec;
vec.push(1); vec.push(2); vec.push(3);
while (!vec.empty()) std::cout << vec.pop() << '\n';
3
2
1
ContainerT<int, std::stack> stk;
stk.push(1); stk.push(2); stk.push(3);
while (!stk.empty()) std::cout << stk.pop() << '\n';
3
2
1

Live Demoна колиру

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