From fcd294a9ec2a17b3512d03e84ae3560d7fcd3f74 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 6 Oct 2022 12:36:27 +0200 Subject: [PATCH] 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 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qtmetamacros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/kernel/qtmetamacros.h b/src/corelib/kernel/qtmetamacros.h index 8a880f34a5..79e3375ed5 100644 --- a/src/corelib/kernel/qtmetamacros.h +++ b/src/corelib/kernel/qtmetamacros.h @@ -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 */