Example:
I have the following code (reduced to model example, Qt library used, Qt classes behavior explained below):
struct Test_impl {
int x;
Test_impl() : x(0) {}
Test_impl(int val) : x(val) {}
};
class Test {
QSharedPointer<Test_impl> m;
public:
Test() : m(new Test_impl()) {}
Test(int val) : m(new Test_impl(val)) {}
void assign(const QVariant& v) {m = v.value<Test>().m; ++m->x;}
~Test(){--m->x;}
};
Q_DECLARE_METATYPE(Test)
QSharedPointer
is a smart pointer implementing move semantic (which is omitted in documentation). QVariant
is somewhat similar to std::any
and has the template method
template<typename T> inline T value() const;
Macro Q_DECLARE_METATYPE
allows values of type Test
to be placed inside a QVariant
.
Problem:
Line m = v.value<Test>().m;
seemingly invokes move assignment for the field m
of a temporary object returned by value()
. After that, Test
destructor is called and promptly crashes trying to access illegal address.
Generally, the problem as I see it is that while move assignment leaves the object itself in a consistent state, state of an object containing moved entity "unexpectedly" changes.
There are several ways to avoid the problem in this particular example I can think of: change Test
destructor to expect null m
or explicitly create temporary Test
object in assign
(MS Visual Studio allows to write std::swap(m, v.value<Test>().m)
, but this code is illegal).
Question(s):
Is there a "proper" (best practice?) way to explicitly invoke copy assignment instead of move? Is there a way to prohibit move semantics for class fields used in the destructor? Why moving a temporary object member was made a default option in the first place?
Aucun commentaire:
Enregistrer un commentaire