Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use QScopedPointer or std::unique_ptr?

Tags:

c++

c++11

qt

c++14

qt5

I'm starting a new project, using Qt5 and QMAKE_CXXFLAGS += -std=c++1y. I'm not sure whether I should prefer QScopedPointer or std::unique_ptr.

I read somewhere that QScopedPointer is not that cool any more.

Does QScopedPointer have any features unique_ptr lacks? Are there any features of unique_ptr that I wouldn't want in when replacing QScopedPointer? Or vice versa?

like image 665
KcFnMi Avatar asked Oct 31 '16 16:10

KcFnMi


People also ask

When should we use Unique_ptr?

When to use unique_ptr? Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another.

What is QScopedPointer?

QScopedPointer is a small utility class that heavily simplifies this by assigning stack-based memory ownership to heap allocations, more generally called resource acquisition is initialization(RAII). QScopedPointer guarantees that the object pointed to will get deleted when the current scope disappears.


2 Answers

QScopedPointer is strictly weaker than unique_ptr as it does not support move semantics.

Its functionality is otherwise extremely similar.

Move semantics are extremely useful, and accidentally using them incorrectly to cause problems is extremely rare. So they vary from harmless to (more typically) helpful.

About the only reason you should use QScopedPointer is interoperability with existing code bases; and even there, given how similar they are, an adapter would be pretty easy.

So if you don't need to adapt, use unique_ptr.


I will now discuss adapting.

The tricky part is the 2nd parameter to QScopedPointer. It very roughly corresponds to the 2nd parameter of unique_ptr.

In unique_ptr stateful deleters are permitted. In QScopedPointer they are not. The

static void cleanup(T* pointer)

corresponds to the

void operator()(T* pointer)const

in the unique_ptr in a pretty one-to-one basis. So:

template<class QDelete>
struct std_deleter {
  template<class T>
  void operator()(T* target) const {
    QDelete::cleanup(target);
  }
};

maps a Qt deleter to a std deleter. The other way is limited by the deleter being stateless:

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);
  }
};

we can now convert:

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
}

which covers about the only reason why you'd use QScopedPointer. There are a few corner cases -- the default deleter QScopedPointer should be converted to a default std::unique_ptr and vice versa.

The array delete QScopedPointer should be converted to a unique_ptr<T[]> and vice versa.

In other cases, I simply wrap up the deleter. In theory, a really fancy trick would be to notice if the incoming deleter was already wrapped up and reverse the wrapping, but if your code is doing that many round-trips there is probably already something wrong.

like image 55
Yakk - Adam Nevraumont Avatar answered Oct 06 '22 04:10

Yakk - Adam Nevraumont


Why would you use something that's not from the standard library compared to something from the standard library?

To me there's only one reason any good programmer would do that: If the external library provides something that the standard library doesn't provide. Is that the case?

Consider your program's portability and updates in the future, and then make that decision.

like image 20
The Quantum Physicist Avatar answered Oct 06 '22 02:10

The Quantum Physicist