C++ Out参数和move语义

C++ Out参数和move语义,c++,c++11,move-semantics,rvalue-reference,C++,C++11,Move Semantics,Rvalue Reference,考虑一种无锁并发数据结构的情况,其中pop()操作需要返回一个项,或者false如果共容器为空(而不是阻塞或抛出)。数据结构是以用户类型T为模板的,它可能很大(但也可能是轻量级的,我希望在这两种情况下都能有效率)T至少必须是可移动的,但我不希望它必须是可复制的 我认为函数签名应该是boolds::pop(T&item),因此该项被提取为out参数,而不是返回值(用于指示成功或失败)。然而,我实际上如何传递它呢?假设有一个底层缓冲区。我会做item=std::move(\u buff[\u tai

考虑一种无锁并发数据结构的情况,其中
pop()
操作需要返回一个项,或者
false
如果共容器为空(而不是阻塞或抛出)。数据结构是以用户类型
T
为模板的,它可能很大(但也可能是轻量级的,我希望在这两种情况下都能有效率)
T
至少必须是可移动的,但我不希望它必须是可复制的

我认为函数签名应该是
boolds::pop(T&item)
,因此该项被提取为out参数,而不是返回值(用于指示成功或失败)。然而,我实际上如何传递它呢?假设有一个底层缓冲区。我会做
item=std::move(\u buff[\u tail])
-移动到引用输出参数中有意义吗?缺点是,用户必须传入一个默认构造的T,这有点违背有效的RAII,因为如果函数失败,结果是一个对象实际上没有初始化它的资源

另一个选项是返回一个
std::pair
,而不是使用out参数,但是对于
返回std::make_pair(false,T)
,还需要一个默认的可构造
T
,它在发生故障时不保存任何资源

第三个选项是将项目作为
std::unique_ptr
返回,但如果
T
是指针或其他轻量级类型,则会产生无用的开销。虽然我可以在数据结构中只存储指针,而实际项目则存储在外部,但这不仅会导致额外的取消引用和缓存未命中,但同时也消除了直接存储在缓冲区中的项所添加的自然填充,并有助于最小化生产者线程和消费者线程命中相同缓存线的可能性。

\35; include
#include <boost/optional.hpp>
#include <string>

template<class T>
struct atomic_queue
{
    using value_type = T;

    auto pop() -> boost::optional<T>
    {
        boost::optional<T> result;

        /*
         * insert atomic ops here, optionally filling result
         */

        return result;
    };

    auto push(T&& arg) -> bool
    {

        /*
         * insert atomic ops here, optionally stealing arg
         */

        return true;
    };

    static auto make_empty_result() {
        return boost::optional<T>();
    }

};

struct difficult {
    difficult(std::string);
    difficult() = delete;
    difficult(difficult const&) = delete;
    difficult& operator=(difficult const&) = delete;
    difficult(difficult &&) = default;
    difficult& operator=(difficult &&) = default;
};

extern void spin();

int main()
{
    atomic_queue<difficult> q;

    auto d = difficult("arg");
    while(not q.push(std::move(d)))
        spin();

    auto popped = q.make_empty_result();
    while(not (popped = q.pop()))
        spin();

    auto& val = popped.get();
}
#包括 模板 结构原子队列 { 使用值_type=T; 自动弹出()->boost::可选 { boost::可选结果; /* *在此处插入原子操作,可以选择填充结果 */ 返回结果; }; 自动推送(T&&arg)->bool { /* *在此处插入原子操作,可以选择窃取arg */ 返回true; }; 静态自动使_为空_结果(){ 返回boost::可选(); } }; 结构困难{ 困难(std::string); 困难()=删除; 困难(困难常数&)=删除; 困难运算符=(困难常量&)=删除; 困难(困难&&)=默认; 难度和运算符=(难度和)=默认值; }; 外空位自旋(); int main() { 原子队列q; 自动d=困难(“arg”); while(不是q.push(std::move(d))) 自旋(); 自动弹出=q.使_为空_结果(); 而(不是(popped=q.pop()) 自旋(); auto&val=popped.get(); }
这就是
boost::optional
,很快将成为
std::optional
。您可以使用
std::aligned\u storage
返回可能包含或不包含对象的存储。这基本上就是
std::optional
的含义