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
的含义