foreach循环中指针到std::unique_ptr的隐式转换

Implicit conversion of pointer to std::unique_ptr in foreach loop

本文关键字:ptr 转换 unique 指针 std foreach 循环      更新时间:2023-10-16

我有一个api调用,它填充了一个由调用者使用的原始指针数组。此函数堆分配每个原始指针,但不分配数组。

无论API函数有多坏,我都无法更改它。

调用api函数代码如下所示:

size_t response_count = api.getResponseCount();
std::vector<Response*> responses(response_count);
api.getResponses(responses.data());
for(auto response : responses) {
// Do some processing with response
delete response;
}

我想将每个响应封装在unique_ptr中,以便在循环迭代结束时仍对其进行清理,而不必显式调用delete。理想情况下,这看起来像:

for(std::unique_ptr<Response> response : responses) {
// Do some processing with response
// No need to delete response, it will be cleaned up as it goes out of scope
}

这不会编译,因为编译器无法将指针转换为unique_ptr:error: conversion from ‘Response*’ to non-scalar type ‘std::unique_ptr<Response>’ requested

有没有办法以这种方式将容器的每个元素强制转换为智能指针,或者我需要显式删除原始指针?

与其将每个指针封装在unique_ptr中,不如考虑使用Boostptr_vector

假设数据已分配,因此您实际上可以使用delete删除它,则代码将如下所示:

size_t response_count = api.getResponseCount();
// Unfortunately, we have to define, then resize. It has a ctor that takes a size,
// but it treats that as an amount to reserve rather than an actual size.
boost::ptr_vector<Response> responses;
responses.resize(response_count);
api.getResponses(responses.c_array());
for(auto response : responses) {
// Do some processing with response
}

并且当responses超出范围时,它将删除它所包含的指针所指向的所有对象。如有必要,可以指定一个Allocator类,用于定义如何分配和删除对象。

参考

https://www.boost.org/doc/libs/1_71_0/libs/ptr_container/doc/ptr_container.html

虽然不是很好的实践,但您可以使用非显式构造函数从unique_ptr派生。

template<typename P>
struct MakeUnique : std::unique_ptr<P> {
MakeUnique(P* p) : std::unique_ptr<P>(p) {}
};

然后可以这样使用:

for ( MakeUnique<Response> resp : responses ) {
...
}

可能是最接近一句话的了。请参阅此处的工作版本。

您可以为vector<response*>创建一个包装器,用于分发唯一指针

struct wrapper{
struct iterator {
iterator( std::vector<response*>::iterator it ) : it_(it){}
friend bool operator!=( iterator const& lhs, iterator const &rhs ){ return lhs.it_ != rhs.it_; }
void operator++(){ ++it_;}
std::unique_ptr<response> operator*(){ return std::unique_ptr<response>(*it_); }
private:   
std::vector<response*>::iterator it_;
};
wrapper( std::vector<response*>& rs ) : rs_{rs} {}
iterator begin() const { return iterator{rs_.begin()}; }
iterator end() const { return iterator{rs_.end()}; }
private:
std::vector<response*>& rs_;
};

然后,您可以像这样迭代响应:

for( auto resp : wrapper( responses ) ){...
}

请参阅此处的工作版本。