提问人:f1msch 提问时间:11/16/2023 更新时间:11/16/2023 访问量:67
如何设计一个模板来访问第一个元素 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()?
问:
当我设计模板时
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;但是,如果我想用 访问第一个元素,则 的基本功能是 而 是 。那么如何设计访问第一个元素呢?QueueType
T& get()
std::queue
front()
std::priority_queue
top()
T& get()
答:
在 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
评论
if constexpr
const &T
&T
您可以委托重载。
假设您希望两个实例化具有相同的接口,则必须返回一个 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); }
如果你想让实例化有一个可变的,你需要像这样的东西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); }
但我认为这可能会变得非常混乱。
评论
priority_queue
const_reference top() const;
T&
一个可以提供专业化,一个可以提供重载(我认为后者优于前者)——但也可以查看模板参数类是否确实提供了适当的函数!
此类测试的非常简单的变体可能如下所示:
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::vector
std::string
front
top
在godbolt上演示。
评论