简体   繁体   中英

C++ Interfacing queue or priority_queue as template parameter of a class

The explanation

I have a class (named Banana in the example) that would receive as a template argument (named Q in the example) a std::queue or a std::priority_queue . The only methods that are required for this argument are push() , pop() and front() . Now the problem: both queues have push() and pop() , but the front() method in the std::priority_queue (the equivalent one) is named top() . How can I interface this parameter Q ?

Possible solutions

I'm thinking about different solutions, but none of these convinces me. I'm writing a C++ library and I don't want dirty solutions that would complicate the life of the library user. Here is what I've thought:

  • Create a sub-class of std::priority_queue that implements front() method. That's dirty.
  • Add another template argument that accepts a function like:

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

    or

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

    depending on the used queue type. Dirty: complicates life to the library user.

  • ...

Do you have one that's simple and elegant?

The example

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

Obviously this won't compile. But I'm posting the compiler response to explain better the problem:

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();

This is only a simplified example. The real problem is much more complex.

You might use the SFINAE way, something like:

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

Add a level of indirection:

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

And let overload resolution do its thing.

I don't know if it make sense but with C++17 you could use std::experimental::is_detected like following:

#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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM