Implement move-ctor and move-assignment-op for QScopedPointer

It makes sense for a QScopedPointer to be movable, for instance
for allowing instances to be returned from a function.
Ownwership of the managed pointer is still tied to one (and one only)
QScopedPointer instance.

Moreover, a move assignment operator makes sense as well, as it
implementing the equivalent of

  this->reset(other.take());

only when other is a rvalue and not a lvalue (so either it's a temporary
or it's getting explicitly moved in with std::move).

This makes QScopedPointer API's a bit closer to std::unique_ptr's one.

Task-number: QTBUG-29754
Change-Id: If1ac0c688327a67af4ad5b7ad45b439b022ed1c6
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Giuseppe D'Angelo 2013-07-27 00:58:27 +02:00 committed by The Qt Project
parent 69b31f7b65
commit 5b9006bbdb
4 changed files with 196 additions and 0 deletions

View File

@ -130,6 +130,33 @@ QT_BEGIN_NAMESPACE
Constructs this QScopedPointer instance and sets its pointer to \a p.
*/
#ifndef Q_QDOC // QTBUG-32675, qdoc can't parse rvalue refs
/*!
\fn QScopedPointer::QScopedPointer(QScopedPointer<T, Cleanup> &&other)
Move-constructs a QScopedPointer instance, making it point at the same
object that \a other was pointing to. \a other is reset to point to \c{NULL}.
\since 5.2
*/
/*!
\fn QScopedPointer<T, Cleanup> &operator=(QScopedPointer<T, Cleanup> &&other)
Move-assigns \a other to this QScopedPointer instance, transferring the
ownership of the managed pointer to this instance.
If \a other and this instance are actually the same object, this operator
does nothing.
Otherwise, this instance is set to point to the object \a other
is pointing to, and \a other is set to point to \c{NULL}.
If this instance was pointing to an object, that object is destroyed,
\since 5.2
*/
#endif
/*!
\fn QScopedPointer::~QScopedPointer()

View File

@ -98,6 +98,19 @@ public:
Cleanup::cleanup(oldD);
}
#ifdef Q_COMPILER_RVALUE_REFS
inline QScopedPointer(QScopedPointer<T, Cleanup> &&other)
: d(other.take())
{
}
inline QScopedPointer<T, Cleanup> &operator=(QScopedPointer<T, Cleanup> &&other)
{
reset(other.take());
return *this;
}
#endif
inline T &operator*() const
{
Q_ASSERT(d);

View File

@ -1,4 +1,5 @@
CONFIG += testcase parallel_test
contains(QT_CONFIG, c++11):CONFIG += c++11
TARGET = tst_qscopedpointer
QT = core testlib
SOURCES = tst_qscopedpointer.cpp

View File

@ -42,6 +42,8 @@
#include <QtTest/QtTest>
#include <QtCore/QScopedPointer>
#include <utility>
/*!
\class tst_QScopedPointer
\internal
@ -73,6 +75,7 @@ private Q_SLOTS:
void objectSize();
void comparison();
void array();
void move();
// TODO instanciate on const object
};
@ -458,6 +461,158 @@ void tst_QScopedPointer::array()
QCOMPARE(instCount, RefCounted::instanceCount.load());
}
#ifdef Q_COMPILER_RVALUE_REFS
struct CountedInteger
{
CountedInteger(int i) : i(i)
{
++instanceCount;
}
~CountedInteger()
{
--instanceCount;
}
CountedInteger(const CountedInteger &c)
: i(c.i)
{
++instanceCount;
}
static int instanceCount;
int i;
};
int CountedInteger::instanceCount = 0;
QScopedPointer<CountedInteger> returnScopedPointer(int i)
{
return QScopedPointer<CountedInteger>(new CountedInteger(i));
}
#endif
void tst_QScopedPointer::move()
{
#ifndef Q_COMPILER_RVALUE_REFS
QSKIP("This test requires rvalues refs");
#else
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p = returnScopedPointer(42);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 42);
QCOMPARE(CountedInteger::instanceCount, 1);
QScopedPointer<CountedInteger> q = returnScopedPointer(51);
QVERIFY(!q.isNull());
QCOMPARE(q->i, 51);
QCOMPARE(CountedInteger::instanceCount, 2);
q = returnScopedPointer(123);
QVERIFY(!q.isNull());
QCOMPARE(q->i, 123);
QCOMPARE(CountedInteger::instanceCount, 2);
p = std::move(q);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 123);
QVERIFY(q.isNull());
QCOMPARE(CountedInteger::instanceCount, 1);
p = std::move(q);
QVERIFY(p.isNull());
QVERIFY(q.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
}
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p = returnScopedPointer(1024);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 1024);
QCOMPARE(CountedInteger::instanceCount, 1);
p.reset();
QVERIFY(p.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
p = returnScopedPointer(1024);
const CountedInteger * const rawPtr = p.data();
p = std::move(p);
// now p is in a "valid, but unspecified state". so the test must not crash.
// we do actually know that this move should've been a noop.
QVERIFY(!p.isNull());
QCOMPARE(p.data(), rawPtr);
QCOMPARE(p->i, 1024);
QCOMPARE(CountedInteger::instanceCount, 1);
p.reset();
QVERIFY(p.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
}
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p = returnScopedPointer(-1);
QVERIFY(!p.isNull());
QCOMPARE(p->i, -1);
QCOMPARE(CountedInteger::instanceCount, 1);
}
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p = returnScopedPointer(0);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 0);
QCOMPARE(CountedInteger::instanceCount, 1);
QScopedPointer<CountedInteger> q;
QVERIFY(q.isNull());
p = std::move(q);
QVERIFY(p.isNull());
QVERIFY(q.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
}
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p;
QVERIFY(p.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
QScopedPointer<CountedInteger> q = returnScopedPointer(123);
QVERIFY(!q.isNull());
QCOMPARE(q->i, 123);
QCOMPARE(CountedInteger::instanceCount, 1);
p = std::move(q);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 123);
QVERIFY(q.isNull());
QCOMPARE(CountedInteger::instanceCount, 1);
}
QCOMPARE(CountedInteger::instanceCount, 0);
{
QScopedPointer<CountedInteger> p;
QVERIFY(p.isNull());
QCOMPARE(CountedInteger::instanceCount, 0);
p = returnScopedPointer(2001);
QVERIFY(!p.isNull());
QCOMPARE(p->i, 2001);
QCOMPARE(CountedInteger::instanceCount, 1);
}
QCOMPARE(CountedInteger::instanceCount, 0);
#endif
}
QTEST_MAIN(tst_QScopedPointer)
#include "tst_qscopedpointer.moc"