5

Here we can read that no copy construct and copy assignment operator evaluable. But here we can read that qRegisterMetaType and Q_DECLARE_METATYPE have to have public default constructor, public copy constructor and public destructor. The question is: who is telling a lie? Or I did not understand it correctly?

VALOD9
  • 566
  • 2
  • 6
  • 22

3 Answers3

6

Everything is true:
1. QObject can't be copied and all its descendants can't be copied also.
2. Q_DECLARE_METATYPE accepts objects with public constructor, copy constructor and destructor.

There is no contradiction, because you can't register QObject descendants with Q_DECLARE_METATYPE.

EDIT:

When you convert your class to QVariant it uses a copy constructor to make a copy of your object:

 void *ptr = QMetaType::construct(x->type, copy);
nobody
  • 19,814
  • 17
  • 56
  • 77
Ezee
  • 4,214
  • 1
  • 14
  • 29
  • But why my class must have copy-constructor to work like QObject? I mean storing in QVariant. – VALOD9 Sep 17 '14 at 12:46
  • 3
    Yes, to be stored in `QVariant` your class must have a copy constructor, but it is not "work like QObject", because QObject, QWidget... can't be stored in `QVariant` – Ezee Sep 17 '14 at 12:48
  • Added explanation about copy constructor to the answer. – Ezee Sep 17 '14 at 13:00
  • can you also explain me why I cant store QObject in a QVariant? – VALOD9 Sep 17 '14 at 13:04
  • The reason is the same that you can't write `QObject o1 = o2`. It doesn't have state, it can't be serialized, deserialized, copied. It's not a kind of storage. `QThread` is also `QObject`. Could you imagine serializing of a thread?) – Ezee Sep 17 '14 at 13:10
  • So look at `QObject` not as storage but as process, that can't be suspended. – Ezee Sep 17 '14 at 13:16
  • But what about move semantics? Can I create QVariant from QObject&&? – VALOD9 Sep 17 '14 at 14:26
  • 1
    No, you can't. But you can create it with a pointer to QObject. However, if you think of copying QObject, I suggest to you consider moving all variables of you object to a `struct` State and then create `QVariant` with this state, then create a new object with the same state. – Ezee Sep 17 '14 at 18:23
2

Q_DECLARE_METATYPE macro is used to generate information for custom user types, if you want to use them as SIGNAL/SLOT arguments.

Example:

struct MyInfo
{
  QString name;
  QDate birthday;
};

Q_DECLARE_METATYPE( MyInfo )

// ... somewhere in cpp:
{
  QObject::connect( obj1, SIGNAL( newData( MyInfo ), SLOT( onNewData( MyInfo ) ) );
}

Without Q_DECLARE_METATYPE macro you could not pass MyInfo as signal or slot argument.

If you use cross-thread connections (Qt::QueuedConnection, Qt::BlockingQueuedConnection, etc) you also need to register your type with qRegisterMetatype<MyInfo>(); call.


But it is ok, to use Q_DECLARE_METATYPE to register POINTERS to QObjects. For example:

class MyItem
  : public QObject
{
  Q_OBJECT
//...
};

Q_DECLARE_METATYPE( MyItem * )
// or event better Q_DECLARE_METATYPE( QSharedPointer< MyItem > )

Pointers are POD types, that may be constructed, copied, etc.

Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
2

You can certainly implement a copy constructor and assignment operators in a class that derives from QObject, but you can't refer to the deleted base class copy constructor and assignment operator. You need to roll your own.

Thus, you need to live with the fact that the copy construction or assignment won't affect the signal/slot connections to either the source or the target object. You must also decide how the parentage is handled when copying - either of the three possible choices are entirely arbitrary - that's why it makes no sense to copy objects in most cases, it's too error prone.

For example, using the copy-and-swap idiom.

class CopyableObject : public QObject
{
  Q_OBJECT
public:
  friend void swap(CopyableObject & first, CopyableObject & second) {
    // d_ptr swap doesn't take care of parentage
    QObject * firstParent = first.parent();
    QObject * secondParent = second.parent();
    first.setParent(0);
    second.setParent(0);
    first.d_ptr.swap(second.d_ptr);
    second.setParent(firstParent);
    first.setParent(secondParent);
  }
  CopyableObject(const CopyableObject & other) : QObject(other.parent()) {
    Q_ASSERT(thread() == other.thread());
    setObjectName(other.objectName());
    blockSignals(other.signalsBlocked());
  }
  CopyableObject(QObject * parent = 0) : QObject(parent) {}
  // C++11 only
  #if __cplusplus >= 201103L
  CopyableObject(CopyableObject && other) : CopyableObject() {
    swap(*this, other);
  }
  #endif
  CopyableObject & operator=(CopyableObject other) {
    swap(*this, other);
    return *this;
  }
};

Note that you need to reimplement the swap function for derived classes.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313