将左值传递给右值

The*_*eAJ 8 c++ rvalue lvalue c++11

我做了一个小的"阻塞队列"课程.令我恼火的是,我为传递给enqueue成员函数的值创建了冗余代码.

以下两个函数执行相同的操作(除了rvalue使用std :: move将rvalue移动到实际的队列集合中),除了句柄lvalue和rvalue:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

我的问题是,有没有办法结合这两个函数,而不会失去对右值引用的支持.

How*_*ant 15

这是完美前进的典型例子.通过模板化函数(成员模板,如果这是一个成员函数)来做到这一点:

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();
}
Run Code Online (Sandbox Code Playgroud)

说明:如果您将左值传递Tenqueue,U将推导出T&,并将forward它作为左值传递,您将获得所需的复制行为.如果你传递一个右值Tenqueue,U将演绎到T,并且forward将沿着把它作为一个右值,你会得到你想要的移动行为.

这比"按值传递"方法更有效,因为您永远不会进行不必要的复制或移动.关于"按值传递"方法的缺点是函数接受任何东西,即使它是错误的.你可能会或可能不会得到级联错误push.如果这是一个问题,您可以enable_if enqueue限制它将实例化的参数.

根据评论更新

根据下面的评论,这就是我理解的东西:

#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);
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好.但是如果你试图将双排队:

q.enqueue(1.0);
Run Code Online (Sandbox Code Playgroud)

这仍然有效,因为double可以隐式转换为int.但是,如果你不想让它工作怎么办?然后你可以限制你enqueue这样:

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();
}
Run Code Online (Sandbox Code Playgroud)

现在:

q.enqueue(1.0);
Run Code Online (Sandbox Code Playgroud)

结果是:

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.
Run Code Online (Sandbox Code Playgroud)

q.enqueue(1);仍然可以正常工作.即限制您的成员模板是您需要做出的设计决策.什么U你要enqueue接受?没有正确或错误的答案.这是一项工程判断.还有其他一些可能更合适的测试(例如std :: is_convertible,std :: is_constructible等).对于您的应用程序来说,正确的答案可能完全没有约束,正如上面的第一个原型.


ipc*_*ipc 7

在我看来,这enqueue(const&)enqueue(&&)是一个特例enqueue_emplace.任何好的C++ 11类容器都有这三个功能,前两个是第三个的特例.

void enqueue(const T& item) { enqueue_emplace(item); }
void enqueue(T&& item)      { enqueue_emplace(std::move(item)); }

template <typename... Args>
void enqueue_emplace(Args&&... args)
{
    std::unique_lock<std::mutex> lock(m);
    this->emplace(std::forward<Args>(args)...); // queue already has emplace
    this->data_available = true;
    cv.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单但有效的解决方案,它应该与原始界面的行为相同.它也优于模板方法,因为您可以将初始化列表排入队列.


老帖子:按价值做好老传:

void enqueue(T item)
{
    std::unique_lock<std::mutex> lock(m);
    this->push(std::move(item));
    this->data_available = true;
    cv.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

enqueue"拥有"它的参数,并希望将其移入队列.通过价值传递是正确的概念来说明这一点.

它只做一个副本和一个动作.不幸的是T,对于没有优化移动构造函数的s,这可能会很慢.我认为由于这个原因,标准库中的容器总是有两个重载.但另一方面,一个好的编译器可能会优化它.