通过 API 填写"std::optional"<T>,获取输出"T*"参数

Filling `std::optional<T>` via API taking output `T*` parameter

本文关键字:参数 输出 获取 gt lt API 填写 std optional 通过      更新时间:2023-10-16

我有一个传统的非阻塞取消排队API,如下所示:

template <typename T>
bool bad_queue<T>::try_dequeue(T* out);
// Returns 'false' if there was nothing to dequeue.
// Returns 'true' and sets '*out' to the dequeued item otherwise.

我想将bad_queue<T>包装到我自己的cool_queue<T>中,它从其try_dequeue成员函数返回一个std::optional<T>

template <typename T>
class cool_queue
{
private:
bad_queue<T> _q;
public:
std::optional<T> try_dequeue();
};

这是我目前实现try_dequeue的方式:

template <typename T>
std::optional<T> cool_queue<T>::try_dequeue()
{
T temp;
const auto ok = _q.try_dequeue(&temp);
if(!ok) { return std::nullopt; }
return {std::move(temp)};
} 

有没有办法避免临时temp缓冲区并直接构造_q.try_dequeue在可选中返回的值?例:

// PSEUDOCODE
template <typename T>
std::optional<T> cool_queue<T>::try_dequeue()
{
std::optional<T> temp;
const auto ok = _q.try_dequeue(&temp.storage());
if(!ok) { return std::nullopt; }
temp.assume_has_object();
return temp;
} 

你不能通过指针真正构造可选的值,但你的遗留 API 无论如何都不允许这样做。相反,您可以在 optional 中默认构造一个值,并将指针传递给该值:

template <typename T>
std::optional<T> cool_queue<T>::try_dequeue()
{
std::optional<T> temp;
const auto ok = _q.try_dequeue(&temp.emplace());
if (!ok) { temp.reset(); }
return temp;
}

从您的描述来看,旧版try_dequeue函数似乎没有任何初始化为目标对象分配的内存的责任("...将"*out'设置为 ...">),因此需要在旧版(可能是对象分配)调用之前构造目标对象。具体来说,对于目标对象是std::optional实例包装的值,如果我们要访问其地址,该实例必须包含一个(包装的)值。

如果std::optional的观察者不包含值,则对 ([optional.observe]) 的要求隐式指定 UB;如果*someOptional不包含值;从 [optional.optional]/1 开始:

任何给定时间的任何optional<T>实例都包含 值或不包含值。当一个实例可选 包含一个值,表示 T 类型的对象,称为 可选对象的包含值在存储中分配 的可选对象。

绕过此问题的唯一方法是更改旧版 API。

正如@Casey的注释中所述,您可以就地对目标对象进行值初始化,并相应地重构cool_queue<T>::try_dequeue(),例如:

template <typename T>
std::optional<T> cool_queue<T>::try_dequeue()
{  
std::optional<T> temp{std::in_place};
return _q.try_dequeue(&*temp) ? temp : std::nullopt;
}