Шаблонная функция для вывода stack, queue и priority_queue

84
16 февраля 2021, 19:20

Нужна шаблонная функция, которая принимает один из трех контейнеров и печатает его содержимое.

Проблема в том, что у контейнеров разные методы получения элементов.

Мое решение работает если использовать его только для стека, но как только я пытаюсь проделать то же самое для очередей, то получаю ошибку компиляции о невозможности выполнения static_cast.

Что я делаю не так, и есть ли не такой костыльный выриант решения?

template <typename T1> 
void Print_sqp(typename T1 t)
{
        const std::type_info& info = typeid(t);
        const std::type_info& info_stack = typeid(std::stack<T1::value_type>);
        const std::type_info& info_queue = typeid(std::queue<T1::value_type>);
        const std::type_info& info_p_queue = typeid(std::priority_queue<T1::value_type>);
        if (info == info_stack)
        {
            auto tmp = static_cast<std::stack<T1::value_type>>(t);
            while (!(tmp.empty()))
            {
                std::cout << tmp.top() << " ";
                tmp.pop();
            }
            std::cout << std::endl;
        }
        else if (info == info_queue)
        {
            auto tmp = static_cast<std::queue<T1::value_type>>(t);
            while (!tmp.empty())
            {
                std::cout << tmp.front() << " ";
                tmp.pop();
            }
            std::cout << std::endl;
        }
        else if (info == info_p_queue)
        {
            auto tmp = static_cast<std::priority_queue<T1::value_type>>(t);
            while (!tmp.empty())
            {
                std::cout << tmp.top() << " ";
                tmp.pop();
            }
            std::cout << std::endl;
        }
    }
Answer 1

Дело в том, что stack/queue/priority_queue - это не контейнеры, а ADT. Контейнер, хранящий данные указывается в параметре шаблона.

Для queue:

template<
    class T,
    class Container = std::deque<T>
> class queue;

Поэтому для них придётся отдельно специализацию писать.

Может быть есть другие способы, но я не большой эксперт в шаблонах.

Специализация для ADT:

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

template <typename T1>
void Print_sqp(const T1& t)
{
    for (const auto& i: t){
        std::cout << i << " ";
    }
    std::cout << std::endl;
}
template<typename T1>
void Print_sqp(const std::stack<T1>& t1){
    auto t = t1;
    while (!(t.empty()))
    {
        std::cout << t.top() << " ";
        t.pop();
    }
    std::cout << std::endl;
}
template<typename T1>
void Print_sqp(const std::queue<T1>& t1){
    auto t = t1;
    while (!t.empty())
    {
        std::cout << t.front() << " ";
        t.pop();
    }
    std::cout << std::endl;
}
template<typename T1>
void Print_sqp(const std::priority_queue<T1>& t1){
   auto t = t1;
    while (!t.empty())
    {
        std::cout << t.top() << " ";
        t.pop();
    }
    std::cout << std::endl;
}

int main()
{
    std::stack<int> st;
    st.push(1);
    std::queue<int> qu;
    qu.push(2);
    std::priority_queue<int> pqu;
    pqu.push(3);
    std::vector<int> v;
    v.push_back(4);
    Print_sqp(st);
    Print_sqp(qu);
    Print_sqp(pqu);
    Print_sqp(v);
    return 0;
}

По наводке от AnT посмотрел внимательнее описание std::stack, там есть protected поле Container c

То есть вот так можно ( в дополнении к тому примеру, что перед этим):

...
template<typename T>
class MyStack: public std::stack<T>{
public:
    const typename std::stack<T>::container_type& getContainer() const{
        return this->c;
    }
};
int main()
{
    MyStack<int> st;
    st.push(1);
...
    Print_sqp(st.getContainer());
...

А можно (но выглядит совсем уже неприлично/неправильно) и так:

template<typename T>
class Wrapper: public T{
public:
    const typename T::container_type& getContainer() const{
        return this->c;
    }
};

template<typename T1>
void Print_sqp(const std::stack<T1>& t1){
    const auto ptr = static_cast<const Wrapper<std::stack<T1>>*>(&t1);
    Print_sqp(ptr->getContainer());
}
template<typename T1>
void Print_sqp(const std::queue<T1>& t1){
    const auto ptr = static_cast<const Wrapper<std::queue<T1>>*>(&t1);
    Print_sqp(ptr->getContainer());
}
template<typename T1>
void Print_sqp(const std::priority_queue<T1>& t1){
    const auto ptr = static_cast<const Wrapper<std::priority_queue<T1>>*>(&t1);
    Print_sqp(ptr->getContainer());
}
Answer 2

То, что вы пытаетесь сделать, может быть сделано именно таким способом в С++17. Но записывается это не так и typeid вам тут не поможет. Здесь нужен if constexpr

#include <type_traits>
#include <stack>
#include <queue>
#include <iostream>
template <typename T> 
void Print_sqp(T t)
{
  using E = typename T::value_type;
  using C = typename T::container_type;
  if constexpr (std::is_same<T, std::stack<E, C>>::value)
  {
    while (!t.empty())
    {
      std::cout << t.top() << " ";
      t.pop();
    }
    std::cout << std::endl;  
  }
  else if constexpr (std::is_same<T, std::queue<E, C>>::value) 
  {
    while (!t.empty())
    {
      std::cout << t.front() << " ";
      t.pop();
    }
    std::cout << std::endl;  
  }
  else
  {
    using CMP = typename T::value_compare;
    if constexpr (std::is_same<T, std::priority_queue<E, C, CMP>>::value)
    {
      while (!t.empty())
      {
        std::cout << t.top() << " ";
        t.pop();
      }
      std::cout << std::endl;  
    }
  }
}
int main()
{
  std::stack<int> s;
  s.push(3); s.push(1); s.push(2); 
  Print_sqp(s);
  std::queue<int> q;
  q.push(3); q.push(1); q.push(2); 
  Print_sqp(q);
  std::priority_queue<int> pq;
  pq.push(3); pq.push(1); pq.push(2); 
  Print_sqp(pq);
}

При желании вы, однако, можете пойти "классическим" путем без if constexpr, через перегруженные функции

#include <type_traits>
#include <stack>
#include <queue>
#include <iostream>
template <typename E, typename C> 
void Print_sqp(std::stack<E, C> t)
{
  while (!t.empty())
  {
    std::cout << t.top() << " ";
    t.pop();
  }
  std::cout << std::endl;  
}
template <typename E, typename C> 
void Print_sqp(std::queue<E, C> t)
{
  while (!t.empty())
  {
    std::cout << t.front() << " ";
    t.pop();
  }
  std::cout << std::endl;  
}
template <typename E, typename C, typename CMP> 
void Print_sqp(std::priority_queue<E, C, CMP> t)
{
  while (!t.empty())
  {
    std::cout << t.top() << " ";
    t.pop();
  }
  std::cout << std::endl;  
}
int main()
{
  std::stack<int> s;
  s.push(3); s.push(1); s.push(2); 
  Print_sqp(s);
  std::queue<int> q;
  q.push(3); q.push(1); q.push(2); 
  Print_sqp(q);
  std::priority_queue<int> pq;
  pq.push(3); pq.push(1); pq.push(2); 
  Print_sqp(pq);
}
READ ALSO
Как можно сделать метод быстрее? [закрыт]

Как можно сделать метод быстрее? [закрыт]

Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы на него можно было дать ответ, основанный на фактах и цитатах

100
Почему происходит ошибка компилятора?

Почему происходит ошибка компилятора?

Вылазит MSB6006 " read access violation**IsRegistered** was nullptr VS 2019

86
Задержка в конце нескольких действий

Задержка в конце нескольких действий

Всем приветВ одной функции есть два действия

79