问题描述
我做了一个小的阻塞队列"类.让我感到恼火的是,我为传递给 enqueue
成员函数的值创建了冗余代码.
I made a small 'blocking queue' class. It irritates me that I have created redundant code for values passed into the enqueue
member function.
以下是两个功能完全相同的函数(除了rvalue使用std :: move将rvalue移动到实际的队列集合中),分别处理lvalue和rvalue的情况除外:
Here are the two functions that do the same exact thing (except the rvalue uses std::move to move the rvalue into the actual queue collection), except handles lvalue and rvalue respectively:
void enqueue(const T& item)
{
std::unique_lock<std::mutex> lock(m);
this->push(item);
this->data_available = true;
cv.notify_one();
}
void enqueue(T&& item)
{
std::unique_lock<std::mutex> lock(m);
this->push(std::move(item));
this->data_available = true;
cv.notify_one();
}
我的问题是,有没有一种方法可以在不失去对右值引用的支持的情况下组合这两个函数.
My question is, is there a way to combine these two functions, without losing support for rvalue references.
推荐答案
这是需要完美转发的经典示例.通过模板化函数(如果是成员函数,则为成员模板)来完成此操作:
This is a classic example of the need to perfectly forward. Do this by templating the function (member template if this is a member function):
template <class U>
void enqueue(U&& item)
{
std::unique_lock<std::mutex> lock(m);
this->push(std::forward<U>(item));
this->data_available = true;
cv.notify_one();
}
说明:如果将左值 T
传递给入队
,则 U
将推导为 T&
,然后 forward
会将其作为左值传递,您将获得所需的复制行为.如果将右值 T
传递给 Tenqueue
,则 U
会推导为 T
,而 forward
会将其作为右值传递,您将获得所需的移动行为.
Explanation: If you pass an lvalue T
to enqueue
, U
will deduce to T&
, and the forward
will pass it along as an lvalue, and you'll get the copy behavior you want. If you pass an rvalue T
to enqueue
, U
will deduce to T
, and the forward
will pass it along as an rvalue, and you'll get the move behavior you want.
这比按值传递"方法更有效,因为您永远不会进行不必要的复制或移动.与按值传递"方法相比,不利之处在于该函数接受任何内容,即使它是错误的也是如此.您可能会或可能不会在 push
下获得级联错误.如果这是一个问题,则可以 enable_if enqueue
来限制其实例化的参数.
This is more efficient than the "pass by value" approach in that you never make an unnecessary copy or move. The downside with respect to the "pass by value" approach is that the function accepts anything, even if it is wrong. You may or may not get cascaded errors down under the push
. If this is a concern, you can enable_if enqueue
to restrict what arguments it will instantiate with.
根据评论进行更新
根据下面的评论,这就是我所了解的样子:
Based on the comments below, this is what I understand things look like:
#include <queue>
#include <mutex>
#include <condition_variable>
template <class T>
class Mine
: public std::queue<T>
{
std::mutex m;
std::condition_variable cv;
bool data_available = false;
public:
template <class U>
void
enqueue(U&& item)
{
std::unique_lock<std::mutex> lock(m);
this->push(std::forward<U>(item));
this->data_available = true;
cv.notify_one();
}
};
int
main()
{
Mine<int> q;
q.enqueue(1);
}
这一切都很好.但是,如果您尝试加入双排,该怎么办:
This is all good. But what if you try to enqueue a double instead:
q.enqueue(1.0);
这仍然有效,因为double可以隐式转换为int.但是,如果您不希望它起作用,该怎么办?然后,您可以像这样限制您的 enqueue
:
This still works because the double is implicitly convertible to the int. But what if you didn't want it to work? Then you could restrict your enqueue
like this:
template <class U>
typename std::enable_if
<
std::is_same<typename std::decay<U>::type, T>::value
>::type
enqueue(U&& item)
{
std::unique_lock<std::mutex> lock(m);
this->push(std::forward<U>(item));
this->data_available = true;
cv.notify_one();
}
现在:
q.enqueue(1.0);
导致:
test.cpp:31:11: error: no matching member function for call to 'enqueue'
q.enqueue(1.0);
~~^~~~~~~
test.cpp:16:13: note: candidate template ignored: disabled by 'enable_if' [with U = double]
std::is_same<typename std::decay<U>::type, T>::value
^
1 error generated.
但是 q.enqueue(1);
仍然可以正常工作.IE.限制成员模板是您需要做出的设计决定.您要 enqueue
接受什么 U
?没有正确或错误的答案.这是工程上的判断.还有其他一些可能更合适的测试(例如std :: is_convertible,std :: is_constructible等).如上第一个原型所示,也许对您的应用程序来说正确的答案根本没有约束.
But q.enqueue(1);
will still work fine. I.e. restricting your member template is a design decision you need to make. What U
do you want enqueue
to accept? There is no right or wrong answer. This is an engineering judgment. And several other tests are available that may be more appropriate (e.g. std::is_convertible, std::is_constructible, etc.). Maybe the right answer for your application is no constraint at all, as first prototyped above.
这篇关于将左值传递给右值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!