如何设计一个模板来访问第一个元素 std::queue.front() 或 std::p riority_queue.top()?

How to design a template to access the first element with either std::queue.front() or std::priority_queue.top()?

提问人:f1msch 提问时间:11/16/2023 更新时间:11/16/2023 访问量:67

问:

当我设计模板时

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

如图所示,是 std::queue 或 std::p riority_queue;但是,如果我想用 访问第一个元素,则 的基本功能是 而 是 。那么如何设计访问第一个元素呢?QueueTypeT& get()std::queuefront()std::priority_queuetop()T& get()

C++ 模板

评论


答:

3赞 ChrisB 11/16/2023 #1

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

#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

评论

1赞 康桓瑋 11/16/2023
哼,为什么不用?if constexpr
0赞 ChrisB 11/16/2023
@康桓瑋:公平点。我最初想为和返回类型敞开大门,但最终没有显示出来。编辑。const &T&T
3赞 molbdnilo 11/16/2023 #2

您可以委托重载。

假设您希望两个实例化具有相同的接口,则必须返回一个 const 引用,因为这是您从 中得到的。get()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); }

如果你想让实例化有一个可变的,你需要像这样的东西queueget()

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

但我认为这可能会变得非常混乱。

评论

1赞 pptaszni 11/16/2023
只有超载的头脑,不能被束缚priority_queueconst_reference top() const;T&
0赞 molbdnilo 11/16/2023
@pptaszni 当然。
2赞 Aconcagua 11/16/2023 #3

一个可以提供专业化,一个可以提供重载(我认为后者优于前者)——但也可以查看模板参数类是否确实提供了适当的函数!

此类测试的非常简单的变体可能如下所示:

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

这种变体的好处:它也适用于任何提供所讨论的两个功能之一的容器,例如 或者(在上面的实现中,如果容器同时提供两者,则优先)。std::vectorstd::stringfronttop

在godbolt上演示。