C ++ Интерфейсная очередь или priority_queue как параметр шаблона класса

Объяснение

У меня есть класс (названный Banana в примере), который получит в качестве аргумента шаблона (с именем Q в примере) std::queue или std::priority_queue, Единственные методы, которые требуются для этого аргумента: push(), pop() а также front(), Теперь проблема: обе очереди имеют push() а также pop(), но front() метод в std::priority_queue (эквивалентный) назван top(), Как я могу связать этот параметр Q?

Возможные решения

Я думаю о разных решениях, но ни одно из них не убеждает меня. Я пишу библиотеку C ++, и я не хочу грязный решения, которые могут осложнить жизнь пользователя библиотеки. Вот что я подумал:

  • Создать подкласс std::priority_queue который реализует front() метод. Это грязно.
  • Добавьте еще один аргумент шаблона, который принимает функцию вроде:

    [] (const std::priority_queue& q) { q.top(); }
    

    или же

    [] (const std::queue& q) { q.front(); }
    

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

У вас есть простой и элегантный?

Пример

#include <iostream>
#include <queue>
#include <utility>

template <typename T, class Q = std::queue<T>>
class Banana
{
private:
Q queue;

public:
void push(T&& o)
{
queue.push(std::move(o));
}

const T& top()
{
return queue.front();
}
};

int main()
{
Banana<int> banana0;
banana0.push(0);
std::cout << banana0.top() << std::endl;

Banana<int, std::priority_queue<int>> banana1;
banana1.push(1);
std::cout << banana1.top() << std::endl;

return 0;
}

Очевидно, это не скомпилируется. Но я публикую ответ компилятора, чтобы лучше объяснить проблему:

test.cxx: In instantiation of ‘const T& Banana<T, Q>::top() [with T = int; Q = std::priority_queue<int>]’:
test.cxx:32:34:   required from here
test.cxx:20:30: error: ‘class std::priority_queue<int>’ has no member named ‘front’
return queue.front();

Это только упрощенный пример. Настоящая проблема гораздо сложнее.

2

Решение

Вы можете использовать способ SFINAE, что-то вроде:

template <typename T, class Q = std::queue<T>>
class Banana
{
private:
Q queue;

template <typename Queue>
static
auto private_top(Queue& queue) -> decltype(queue.top()) { return queue.top();}

template <typename Queue>
static
auto private_top(Queue& queue) -> decltype(queue.front()) { return queue.front();}

public:
void push(T&& o)
{
queue.push(std::move(o));
}

const T& top()
{
return private_top(queue);
}
};
3

Другие решения

Добавьте уровень косвенности:

template<typename T, class C>
auto& front_or_top(std::queue<T, C> const &q) {
return q.front();
}

template<typename T, class C, typename Comp>
auto& front_or_top(std::priority_queue<T, C, Comp> const &q) {
return q.top();
}

И пусть разрешение перегрузки делает свое дело.

2

Я не знаю, имеет ли это смысл, но с C ++ 17 вы могли бы использовать std::experimental::is_detected как следующее:

#include <iostream>
#include <queue>
#include <set>
#include <experimental/type_traits>

template<typename T>
using front_t = decltype( std::declval<T&>().front() );

template<typename T>
constexpr bool has_front = std::experimental::is_detected_v<front_t, T >;

template<class T>
void elementTop(T& obj)
{
if constexpr (has_front<T>)
{
obj.front();
std::cout << "Front \n";
}
else
{
obj.top();
std::cout << "Top \n";
}
}

int main()
{

std::priority_queue<int> q1;
std::queue<int> q2;
std::vector<int> vec;

std::set<int> s;

elementTop(q1) ;
elementTop(q2) ;
elementTop(vec) ;

/* elementTop(s) ; errors out */
return 0;
}

See Here

1
По вопросам рекламы [email protected]