我的几个对象包含 unique_ptr
实例作为成员并指定一个 move 构造函数:
struct Foo {
Foo(Foo&& other): someA(std::move(other.someA)), someB(other.someB){}
unique_ptr<A> someA;
B someB;
}
这意味着我可以 move Foo
对象,但不能复制它们。每当它们超出范围时,默认析构函数将调用 unique_ptr
实例的析构函数:
void someFunc(Foo&& foo);
{
Foo a;
Foo b;
someFunc(Foo()); // compiles
someFunc(std::move(a)); // compiles -> a.someA is now nullptr
someFunc(b); // does not compile, because copy is not allowed
} // <- b is destructed and object guarded by b.someA is deleted
现在,我想将这样一个对象 move 到一个容器中,稍后再获取它。这对于 C++ 容器来说不是问题,因为它们可以处理 move 语义。不幸的是,我的容器是由基于 C 的实时操作系统(FreeRTOS 队列:https://www.freertos.org/xQueueSendToBack.html)提供的。它的访问函数 xQueueSendToBack
和 xQueueReceive
获取指向该项目的 void*
以便使用 memcpy
或等效于将元素移入和移出容器。
我的(简化的)队列包装器解决方案如下所示:
template <typename T, std::size_t N>
struct FreeRtosQueue {
bool sendToBack(T&& item) {
if (xQueueSendToBack(this->frtosQueueHandle, &item, portMAX_DELAY) != pdTRUE) {
return false;
}
return true;
} // <- item destructor called without prior move
T receive() {
T item;
xQueueReceive(this->frtosQueueHandle, &item, portMAX_DELAY);
return item;
}
QueueHandle_t frtosQueueHandle;
};
不幸的是,在 sendToBack
结束时,项目的析构函数将被调用,而不会 move 它(从 C++ 的角度来看)。这会导致项目中由 unique_ptr
保护的所有对象被删除。
- 如何防止队列中的可 move 对象被破坏?
- 您是否发现
receive
实现有任何问题?
最佳答案
将 C++ 对象的引用传递给 C 代码时,通常最好将不透明指针传递给动态分配的对象。这通常由 new
创建。在这种情况下,您可以将 new
与 T
的 move 构造函数一起使用,以获取指向动态分配对象的指针,该对象不属于任何 C++ RAII 对象。然后您可以将此指针本身的拷贝存储在 FreeRTOS 队列中。
bool sendToBack(T &&item) {
T *item_ptr = new T(std::move(item));
if (xQueueSendToBack(this->frtosQueueHandle, &item_ptr, portMAX_DELAY) != pdTRUE) {
delete item_ptr;
return false;
}
return true;
}
请注意,item_ptr
的地址被采用,而不是 item_ptr
本身,作为 xQueueSendToBack()
的 pvItemToQueue
参数是指向要复制到队列中的数据的指针,指针本身的原始字节是应该存储在队列中的内容。这也意味着在创建队列时,您应该在调用 uxQueueCreate()
时将 sizeof(T *)
作为 uxItemSize
传递。
我建议更改 sendToBack
以通过切换到转发引用来支持复制和 move ,如下所示:
template<typename ItemT>
bool sendToBack(ItemT &&item) {
T *item_ptr = new T(std::forward<ItemT>(item));
if (xQueueSendToBack(this->frtosQueueHandle, &item_ptr, portMAX_DELAY) != pdTRUE) {
delete item_ptr;
return false;
}
return true;
}
最后,要从队列中接收一个项目,可以按如下方式完成:
T receive() {
T *item_ptr;
xQueueReceive(this->frtosQueueHandle, &item_ptr, portMAX_DELAY);
// Copy / move the item out of the dynamically allocated object and
// into a local object.
T item(std::move(*item_ptr));
// The original object is moved-from but still must be deleted.
delete item_ptr;
return item;
}
如果 T
没有 noexcept
move/复制构造函数,可以通过立即转移所有权 unique_ptr
如下:
T receive() {
T *item_ptr;
xQueueReceive(this->frtosQueueHandle, &item_ptr, portMAX_DELAY);
std::unique_ptr<T> item_unique_ptr(item_ptr);
T item(std::move(*item_ptr));
return item;
}
您手动遍历队列中的项目并删除 FreeRtosQueue
的析构函数中的任何剩余项目也非常重要,因为存储的指向动态分配内存的指针是不受管理的。您还应该禁用该类的复制构造函数(并可选择实现 move 构造函数)。
关于c++ - 如何通过 memcpy 将可 move 类型推送到/从遗留的基于 C 的容器中弹出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52536159/