c++ 如何设计一个模板来使用std::queue.front()或std::priority_queue.top()访问第一个元素?

3z6pesqy  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(95)

当我设计一个模板时

template<typename T, 
typename QueueType,
typename = std::enable_if_t<std::is_same_v<QueueType, std::queue<T>> || std::is_same_v<QueueType, std::priority_queue<T>>>
class ThreadSafeQueue {
private:
QueueType q;
public:
T& get();
};

字符串
如图所示,QueueType是std::queue或std::priority_queue;但是如果我想用T& get()访问第一个元素,std::queue的基本函数是front(),而std::priority_queuetop()。那么如何设计T& get()来访问第一个元素呢?

c7rzv4ha

c7rzv4ha1#

在C++20中,可以使用新的requires clause来执行以下操作:

#include <type_traits>
#include <deque>
#include <queue>

template<
    typename T, 
    typename QueueType,
    typename = std::enable_if_t<
        std::is_same_v<QueueType, std::queue<T>> || std::is_same_v<QueueType, std::priority_queue<T>>
    >
>
class ThreadSafeQueue {
    private:
        QueueType q;
    public:
        T& get() requires (std::is_same_v<QueueType, std::queue<T>>) {
            return q.front();
        }
        const T& get() requires (std::is_same_v<QueueType, std::priority_queue<T>>) {
            return q.top();
        }
};

int main() {
    ThreadSafeQueue<int, std::queue<int>> a;
    ThreadSafeQueue<int, std::priority_queue<int>> b;
    a.get();
    b.get();
}

字符串
演示:https://godbolt.org/z/Wa6x8Kdo9
你也可以在C++17中使用if constexpr

decltype(auto) get() {
    if constexpr (std::is_same_v<QueueType, std::queue<T>>) {
        return q.front();
    }
    else {
        return q.top();
    }
}


演示:https://godbolt.org/z/7vPoWrf1x

pgccezyw

pgccezyw2#

可以委托给重载。
假设您希望两个示例化具有相同的接口,get()必须返回一个const引用,因为这是您从priority_queue::top获得的。
草图:

const T& internal_get(std::queue<T>& q) { return q.front(); }

const T& internal_get(std::priority_queue<T>& q) { return q.top(); }

const T& get() { return internal_get(q); }

字符串
如果希望queue示例化具有可变的get(),则需要类似于

T& internal_get(std::queue<T>& q) { return q.front(); }

const T& internal_get(std::priority_queue<T>& q) { return q.top(); }        

decltype(auto) get() { return internal_get(q); }


但我觉得这会让人很困惑

euoag5mw

euoag5mw3#

一个 * 可以 * 提供专门化,一个 * 可以 * 提供重载(我认为后者比前者更上级)--但是人们也可以看看模板参数类是否提供了适当的函数!
这种测试的一个非常简单的变体可能如下所示:

template <typename T>
auto has_front(T t) -> decltype(t.front());
void has_front(...);

template <typename T>
bool constexpr has_front_v
    = !std::is_void_v<decltype(has_front(std::declval<T>()))>;

template <typename T>
auto has_top(T t) -> decltype(t.top());
void has_top(...);

template <typename T>
bool constexpr has_top_v = !std::is_void_v<decltype(has_top(std::declval<T>()))>;

字符串
实际上,这些测试对于提供相应函数但返回值为void的类会失败。在给定的情况下,这是无关紧要的,因为这样的函数无论如何都不会达到目的。对于一般情况,一个更精确的变体(在测试仅仅存在的意义上)可以如下所示:

template <typename T>
auto has_function(T t) -> decltype(t.function(), std::true_type());
std::false_type has_function(...);

template <typename T>
bool constexpr has_function_v = decltype(has_function(std::declval<T>()))::value;


有了这些测试,你现在可以使用if constexpr来测试哪个函数用于值检索:

template <typename T>
decltype(auto) get(T& t)
{
    if constexpr(has_front_v<T>)
    {
        return t.front();
    }
    else
    {
        static_assert(has_top_v<T>);
        return t.top();
    }
}


此变体的奖励:它也适用于 any 容器提供两个函数之一,例如std::vectorstd::string(在上面的实现中,如果容器同时提供两个函数,则front将优于top)。
godbolt上的演示。

相关问题