QScopedPointer 严格弱于unique_ptr,因为它不支持移动语义。
它的功能在其他方面非常相似。
移动语义非常有用,意外使用它们导致问题的情况极为罕见。所以它们从无害到(更典型地)有用。
您应该使用QScopedPointer 的唯一原因是与现有代码库的互操作性;即使在那里,考虑到它们的相似程度,适配器也很容易。
所以如果你不需要适应,使用unique_ptr。
我现在将讨论适应。
棘手的部分是QScopedPointer 的第二个参数。非常粗略对应unique_ptr的第二个参数。
在unique_ptr 中允许有状态的删除器。在QScopedPointer 他们不是。
static void cleanup(T* pointer)
对应
void operator()(T* pointer)const
在unique_ptr 中非常一对一。所以:
template<class QDelete>
struct std_deleter {
template<class T>
void operator()(T* target) const {
QDelete::cleanup(target);
}
};
将 Qt 删除器映射到 std 删除器。另一种方式受限于删除器是无状态的:
template<class Std_deleter>
struct Qt_deleter {
template<class T>
static void cleanup(T* target) {
static_assert(std::is_empty<Std_deleter>{}, "Only works with stateless deleters");
Std_deleter{}(target);
}
};
我们现在可以转换了:
template<class T, class D>
QScopedPointer<T, Qt_deleter<D>>
to_qt( std::unique_ptr<T, D>&& src ) {
return src.release();
}
template<class T, class D>
QScopedPointer<T, Qt_deleter<D>>
to_qt( std::unique_ptr<T[], D>&& src ) {
return src.release();
}
template<class T>
QScopedPointer<T>
to_qt( std::unique_ptr<T>&& src ) {
return src.release();
}
template<class T>
QScopedPointer<T, QScopedPointerArrayDeleter>
to_qt( std::unique_ptr<T[]>&& src ) {
return src.release();
}
template<
class T, class D, class R=std::unique_ptr<T, std_deleter<D> >
>
to_std( QScopedPointer<T, D>&& src ) {
return R(src.take()); // must be explicit
}
template<class T, class R=std::unique_ptr<T>>
to_std( QScopedPointer<T>&& src ) {
return R(src.take()); // must be explicit
}
template<class T, class R=std::unique_ptr<T[]>>
to_std( QScopedPointer<T,QScopedPointerArrayDeleter >&& src ) {
return R(src.take()); // must be explicit
}
其中介绍了您使用QScopedPointer 的唯一原因。有一些极端情况 - 默认删除器 QScopedPointer 应转换为默认 std::unique_ptr,反之亦然。
数组删除QScopedPointer 应转换为unique_ptr<T[]>,反之亦然。
在其他情况下,我只是将删除器包装起来。理论上,一个非常奇特的技巧是注意传入的删除器是否已经被包装并反转包装,但如果你的代码执行了这么多次往返,那么可能已经有问题了。