QPrivateSignal: disable implicit conversions from initializer_list

The whole point of QPrivateSignal is to forbid anyone but the class
itself from emitting a certain signal. This is a "workaround" introduced
in Qt 5.0, due to the fact that the PMF syntax for connections requires
users to take the address of a signal, and that is only possible if the
signal itself is public. (In fact, signals were protected in Qt 4.)

The Q_OBJECT macro defines the private QPrivateSignal class. A QObject
subclass that wants to "protect" its signal emissions can declare a
signal carrying an argument of type QPrivateSignal:

  signals:
    void aSignal(int, QPrivateSignal);

If the class itself wants to emit the signal, it can do so:

  emit aSignal(42, QPrivateSignal());

But if "someone else" wants to, they can't use the QPrivateSignal type
because it's private.

  emit obj.aSignal(42, SomeClass::QPrivateSignal());  // ERROR

Here's a hair in this soup: list initialization. If a braced-init-list
is used, [over.ics.list] simply *ignores* access control. This allows an
"untyped" initializer-list to match QPrivateSignal and perform aggregate
initialization:

  emit obj.aSignal(42, {}); // works!

This kind of defeats the whole purpose. Therefore: make QPrivateSignal
not an aggregate and give it an explicit default constructor, disabling
copy-list-initialization for it. This means that using `{}` will fail to
compile (class is no longer an aggregate, a constructor must be
selected, and copy-list-initialization will not select an explicit
constructor).

This isn't a complete fix by any means. There's always the possibility
of using enough template magic to extract QPrivateSignal's type (e.g. as
a local alias) and then create an object of that type; but that sounds
extremely unlikely to be happening "by accident" (whilst it's super-easy
to just type {} as the argument and not realize that you were not
supposed to do so).

[ChangeLog][QtCore][Potentially Source-Incompatible Changes] It is no
longer possible to use `{}` to construct a QPrivateSignal object
(specifically, QPrivateSignal's default constructor is now explicit).
This means that emitting a signal that carries a QPrivateSignal argument
(i.e. a "private signal") cannot any longer be done by using something
like `emit aSignal({})`; instead, such usages must be ported to
`emit aSignal(QPrivateSignal());` or equivalent.

Change-Id: Iac379aee3a8adca5a91d5db906a61bfcd0abc89f
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Giuseppe D'Angelo 2022-10-06 12:36:27 +02:00
parent 2ef130a41d
commit fcd294a9ec
1 changed files with 1 additions and 1 deletions

View File

@ -127,7 +127,7 @@ private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal {}; \
struct QPrivateSignal { explicit QPrivateSignal() = default; }; \
QT_ANNOTATE_CLASS(qt_qobject, "")
/* qmake ignore Q_OBJECT */