Изменения в Qt Core
Qt 6 — результат осознанных усилий по повышению эффективности и удобства использования фреймворка.
Мы стараемся поддерживать бинарную и исходную совместимость всех публичных API в каждом релизе. Но некоторые изменения были неизбежны в стремлении сделать Qt лучшим фреймворком.
В этой статье мы обобщаем эти изменения в Qt Core и предоставляем рекомендации по их обработке.
Классы контейнеров
QHash, QMultiHash, QSet
Подпись qHash()
Для пользовательских типов QHash и QMultiHash полагаются на предоставление вами функции qHash() в том же пространстве имен. В Qt 4 и Qt 5 значение возврата и необязательный второй аргумент функции qHash были типа uint. В Qt 6 это size_t.
То есть, вам нужно изменить
uint qHash(MyType x, uint seed);
на
size_t qHash(MyType x, size_t seed);
Это позволяет QHash, QMultiHash и QSet хранить более чем 232 элементов на 64-битных платформах.
Стабильность ссылок
Реализация QHash и QMultiHash в Qt 6 была изменена с подхода, основанного на узлах, на двухэтапную таблицу поиска. Эта конструкция позволяет поддерживать очень низкую накладную стоимость памяти экземпляра хеш-таблицы, одновременно обеспечивая высокую производительность.
Следует отметить одно поведенческое изменение: новая реализация QHash не будет предоставлять стабильных ссылок на элементы в хеш-таблице при необходимости расширения таблицы или удалении записей. Приложения, которые полагаются на такую стабильность, могут столкнуться с неопределенным поведением.
Удаление QHash::insertMulti
В Qt 5 QHash можно было использовать для создания хеш-таблиц с несколькими значениями, используя QHash::insertMulti, а QMultiHash наследовал от QHash.
В Qt 6 типы и варианты использования различаются, а QHash::insertMulti удален.
QVector, QList
До Qt 6 QVector и QList были отдельными классами. В Qt 6 они объединены: реализация Qt 5 QList ушла, и оба класса используют обновленную реализацию QVector. QList — класс с фактической реализацией, а QVector — псевдоним (typedef) для QList.
fromVector() и toVector() QList, и fromList() и toList() QVector больше не предполагают копирование данных в Qt 6. Теперь они возвращают объект, для которого они были вызваны.
Изменения API
Тип размера QList (и, следовательно, QVector) изменен с int на qsizetype. Вместе с типом размера обновляются подписи всех соответствующих методов для использования qsizetype. Это позволяет QList хранить более чем 231 элементов на 64-битных платформах.
При обновлении кодовой базы до Qt 6 это изменение API, скорее всего, приведет к предупреждениям компилятора о сужении преобразований типов. Имея следующий пример кода:
void myFunction(QList<MyType> &data) {
int size = data.size();
// ...
const int pos = getInsertPosition(size);
data.insert(pos, MyType());
// ...
} вам нужно обновить его, используя либо qsizetype или ключевое слово auto:
void myFunction(QList<MyType> &data) {
auto size = data.size();
// ...
const auto pos = getInsertPosition(size);
data.insert(pos, MyType());
// ...
} В качестве альтернативы можно использовать приведение типов и привести все к int или к qsizetype.
Примечание: Если вы хотите создавать сборки как для Qt 5, так и для Qt 6, ключевое слово auto — хорошее решение для покрытия различий в подписях между версиями.
Структура памяти
QList получил несколько изменений, связанных со структурой памяти в Qt 6.
В Qt 5 sizeof(QList<T>) было равно размеру указателя. Теперь лишнее косвенное обращение к указателю удалено, и данные членов QList хранятся непосредственно в объекте. По умолчанию ожидается, что sizeof(QList<T>) будет равен размеру 3 указателей.
Одновременно обновляется и структура памяти элементов. QList теперь всегда хранит свои элементы непосредственно в выделенной области памяти, в отличие от Qt 5, где некоторые объекты выделены отдельно в куче, а указатели на объекты размещаются в QList вместо этого.
Последнее изменение, в частности, влияет на большие объекты. Чтобы получить поведение Qt 5, можно обернуть ваши объекты в умные указатели и хранить эти умные указатели непосредственно в QList. В этом случае тип вашего QList будет QList<MySmartPointer<MyLargeObject>> по сравнению с QList<MyLargeObject> в Qt 5.
Стабильность ссылок
В реализации QVector/QList внесены некоторые изменения. Изменения, связанные с QVector: вставка в начало оптимизирована (аналогично QList в Qt 5). Изменения, связанные с QList: структура памяти для элементов упрощена.
Важно: Эти изменения влияют на стабильность ссылок. В Qt 6 следует учитывать, что любой метод изменения размера или емкости делает все ссылки недействительными, даже когда QList не является неявно разделяемым. Исключение из этого правила документировано явно.
Приложения, которые полагаются на определенную стабильность ссылок, могут столкнуться с неопределенным поведением при обновлении до Qt 6. Следует уделить особое внимание случаям, когда использовался QVector или QList с несовместимой с C структурой массива.
Классы представлений в Qt6
Общий обзор
С Qt6 появляются несколько новых View классов. Существует уже существующий QStringView, теперь дополненный QByteArrayView и за ним следуют специализированный QUtf8StringView и более универсальный QAnyStringView.
Введение в классы представлений на примере QStringView
Класс QStringView предоставляет единое представление UTF-16 строк с подмножеством только для чтения API QString. В отличие от QString, который хранит свою копию строки (возможно, с подсчетом ссылок), QStringView предоставляет представление строки, хранящейся в другом месте.
char hello[]{ "Hello." }; // narrow multi-byte string literal
QString str{hello}; // needs to make a copy of the string literal
QString strToStr(str); // atomic increment involved to not create a copy of hello again
// The above code can be re-written to avoid copying and atomic increment.
QStringView view{ u"Hello." }; // view to UTF-16 encoded string literal
QStringView viewToView{ view }; // view of the same UTF-16 encoded string literal Строка "Hello." хранится в двоичном формате и не выделяется во время выполнения. view — это только представление строки "Hello.", поэтому не нужно создавать копию. При копировании QStringView viewToView наблюдается та же строка, что и копируемый view. Это означает, что viewToView не нужно создавать копию или атомарно увеличивать счетчик. Это представления существующей строки "Hello.".
Представления в качестве аргумента функции
Представления должны передаваться по значению, а не по ссылке на константу.
void myfun1(QStringView sv); // preferred void myfun2(const QStringView &sv); // compiles and works, but slower
Функции манипулирования представлениями
QStringView поддерживает функции, позволяющие манипулировать представлением строки. Это позволяет изменять представление без создания частичной копии просматриваемой строки.
QString pineapple = "Pineapple";
QString pine = pineapple.left(4);
// The above code can be re-written to avoid creating a partial copy.
QStringView pineappleView{ pineapple };
QStringView pineView = pineappleView.left(4); Строки без завершения нулём и строки, содержащие '\0'
QStringView поддерживает как строки с завершением нулём, так и без него. Различие заключается в способе инициализации QStringView:
QChar aToE[]{ 'a', 'b', 'c', 'd', 'e' };
QStringView nonNull{ aToE, std::size(aToE) }; // with length given
QStringView nonNull{ aToE }; // automatically determines the length
QChar fToJ[]{ 'f', 'g', 'h', '\0', 'j' };
// uses given length, doesn't search for '\0', so '\0' at position 3
// is considered to be a part of the string similarly to 'h' and 'j
QStringView nonNull{ fToJ, std::size(fToJ) };
QStringView part{ fToJ }; //stops on the first encounter of '\0' Модель владения представлениями
Так как views не владеют памятью, к которой они ссылаются, необходимо позаботиться о том, чтобы ссылаемые данные (например, принадлежащие QString) сохранялись дольше, чем view на всех путях кода.
QStringView sayHello()
{
QString hello("Hello.");
return QStringView{ hello }; // hello gets out of scope and destroyed
}
void main()
{
QStringView hello{ sayHello() };
qDebug() << hello; // undefined behavior
} Преобразование QStringView в QString
QStringView не будет неявно или явно преобразовываться в QString, но может создать глубокую копию своих данных:
void print(const QString &s) { qDebug() << s; }
void main()
{
QStringView string{ u"string"};
// print(string); // invalid, no implicit conversion
// QString str{ string }; // invalid, no explicit conversion
print(string.toString());
QString str = string.toString(); // create QString from view
} Важные замечания
Используя новые классы представлений, можно добиться значительного повышения производительности во многих случаях использования. Однако важно знать о возможных нюансах. Поэтому важно помнить:
- Представления должны передаваться по значению, а не по ссылке на константу.
- Создание представления с отрицательной длиной является неопределенным поведением.
- Необходимо позаботиться о том, чтобы ссылаемые данные (например, принадлежащие QString) сохранялись дольше, чем представление, на всех путях кода.
Классы, связанные со строками
Класс QStringView
Начиная с Qt6, рекомендуется использовать QStringView вместо QStringRef. QStringView ссылается на непрерывный фрагмент UTF-16 строки, которой он не владеет. Он действует как тип интерфейса для всех видов UTF-16 строк без необходимости сначала создавать QString. Класс QStringView экспонирует почти все методы только для чтения QString и ранее существовавший QStringRef класс.
Примечание: Необходимо позаботиться о том, чтобы ссылаемые данные строки (например, принадлежащие QString) сохранялись дольше, чем QStringView на всех путях кода.
Примечание: Если QStringView оборачивает QString, необходимо соблюдать осторожность, так как в отличие от QStringRef QStringView не будет обновлять внутренний указатель данных после перемещения данных QString.
QString string = ...;
QStringView view{string};
// Appending something very long might cause a relocation and will
// ultimately result in a garbled QStringView.
string += ...; Класс QStringRef
В Qt6 QStringRef был удален из Qt Core. Чтобы облегчить портирование существующих приложений без изменения всей кодовой базы, класс QStringRef не исчез полностью, а был перемещён в модуль Qt5Compat.
Если вы хотите использовать QStringRef далее, вам необходимо связаться с новым модулем Qt5Compat и добавить эту строку в ваш файл qmake .pro:
QT += core5compat
В случае, если вы уже перенесли своё приложение или библиотеку в систему сборки cmake, добавьте следующее в свой CMakeList.txt:
PUBLIC_LIBRARIES
Qt::Core5Compat END_OF_DOCUMENT_MARKER К сожалению, некоторые методы, экспонируемые классом QString и возвращающие QStringRef, не смогли быть перенесены в Qt5Compat. Поэтому может потребоваться ручная портировка. Если ваш код использует одну или несколько из следующих функций, вам необходимо перенести их на использование QStringView или QStringTokenizer. Также рекомендуется использовать QStringView::tokenize вместо QStringView::split в критичных по производительности участках кода.
Измените код, использующий QStringRef:
QString string = ...;
QStringRef left = string.leftRef(n);
QStringRef mid = string.midRef(n);
QStringRef right = string.rightRef(n);
QString value = ...;
const QVector<QStringRef> refs = string.splitRef(' ');
if (refs.contains(value))
return true; на:
QString string = ...;
QStringView left = QStringView{string}.left(n);
QStringView mid = QStringView{string}.mid(n);
QStringView right = QStringView{string}.right(n);
QString value = ...;
const QList<QStringView> refs = QStringView{string}.split(u' ');
if (refs.contains(QStringView{value}))
return true;
// or
const auto refs = QStringView{string}.tokenize(u' ');
for (auto ref : refs) {
if (ref == value)
return true;
} QMutex и связанные классы
В Qt 6, QRecursiveMutex больше не наследуется от QMutex. Это изменение было сделано для повышения производительности как QMutex, так и QRecursiveMutex.
Из-за этих изменений перечисление QMutex::RecursionMode было удалено, а QMutexLocker теперь является шаблонным классом, который может работать как с QMutex, так и с QRecursiveMutex.
QFuture и связанные классы
Класс QFuture
Для предотвращения непреднамеренного использования QFuture, в Qt 6 были внесены некоторые изменения в API QFuture, которые могут привести к проблемам совместимости исходного кода.
Неявные преобразования между QFuture и другими типами
Преобразование QFuture<T> в T было отключено. Оператор приведения типов вызывал QFuture::result(), что может привести к неопределённому поведению, если пользователь переместил результаты из QFuture с помощью QFuture::takeResult() до попытки преобразования. Используйте методы QFuture::result() или QFuture::takeResult() явно, где вам нужно преобразовать QFuture<T> в T.
Неявное преобразование из QFuture<T> в QFuture<void> также было отключено. Если вы действительно намерены выполнить преобразование, используйте явный конструктор QFuture<void>(const QFuture<T> &):
QFuture<int> future = ... QFuture<void> voidFuture = QFuture<void>(future);
Операторы сравнения на равенство
Операторы сравнения на равенство для QFuture были удалены. Они сравнивали указатели d-полей вместо сравнения результатов, что не соответствует ожиданиям пользователя. Если вам нужно сравнить объекты QFuture, используйте методы QFuture::result() или QFuture::takeResult(). Например:
QFuture<int> future1 = ...;
QFuture<int> future2 = ...;
if (future1.result() == future2.result())
// ... Изменения в поведении QFuture и QFutureWatcher
В Qt 6 были внесены некоторые улучшения в QFuture и QFutureWatcher, которые вызвали следующие изменения в поведении:
- После приостановки QFuture или QFutureWatcher (вызовом
pause()илиsetPaused(true)) QFutureWatcher немедленно не прекратит передачу сигналов о готовности прогресса и результатов. В момент приостановки могут быть в процессе вычисления, которые нельзя остановить. Сигналы для таких вычислений могут всё ещё быть переданы после приостановки, вместо того, чтобы быть отложены и сообщены только после следующего возобновления. Чтобы получить уведомление о том, когда приостановка фактически вступила в силу, можно использовать сигнал QFutureWatcher::suspended(). Кроме того, есть новые методыisSuspending()иisSuspended(), чтобы проверить, находится ли QFuture в процессе приостановки или уже находится в приостановленном состоянии. Обратите внимание, что для сохранения согласованности, для как QFuture, так и QFutureWatcher API, связанные с приостановкой, были устаревшими и заменены аналогичными методами с "suspend" в названии. - QFuture::waitForFinished() теперь будет ждать, пока QFuture фактически окажется в состоянии завершения, вместо того, чтобы завершать выполнение, как только оно не будет в состоянии выполнения. Это предотвращает
waitForFinished()от немедленного завершения, если в момент его вызова будущее ещё не запущено. То же самое относится к QFutureWatcher::waitForFinished(). Это изменение не повлияет на поведение кода, использующего QFuture с QtConcurrent. Только код, который использовал его с недокументированнымQFutureInterfaceможет быть затронут.
Класс QPromise
В Qt 6 вместо неофициального QFutureInterface следует использовать новый класс QPromise в качестве «установщика» (setter) в паре с QFuture.
Классы ввода-вывода
Класс QProcess
В Qt 6 перегрузка QProcess::start(), которая интерпретирует строку команды, разбивая её на имя программы и аргументы, переименована в QProcess::startCommand(). Однако перегрузка QProcess::start(), которая принимает одну строку, а также QStringList для аргументов, существует. Поскольку параметр QStringList по умолчанию равен пустому списку, существующий код, передающий только строку, по-прежнему будет компилироваться, но не выполнит процесс, если это полная строка команды, включающая аргументы.
Qt 5.15 ввёл предупреждения об устаревании для соответствующей перегрузки, чтобы облегчить обнаружение и обновление существующего кода:
QProcess process;
// compiles with warnings in 5.15, compiles but fails with Qt 6
process.start("dir \"My Documents\"");
// works with both Qt 5 and Qt 6; also see QProcess::splitCommand()
process.start("dir", QStringList({"My Documents"});
// works with Qt 6
process.startCommand("dir \"My Documents\""); QProcess::pid() и тип Q_PID были удалены; используйте QProcess::processId() для получения идентификатора процесса. Код, использующий родные Win32 API для доступа к данным в Q_PID как структуре Win32 PROCESS_INFORMATION, больше не поддерживается.
Система метатипов
Класс QVariant
QVariant было переписано для использования QMetaType для всех операций. Это подразумевает изменения в поведении некоторых методов:
QVariant::isNull()теперь возвращает толькоtrue, еслиQVariantпуста или содержитnullptr. В Qt 5 она также возвращала true для классов в qtbase, у которых был методisNull, если тот возвращал true. Код, зависящий от старого поведения, должен проверять, возвращает ли содержащееся значение isNull — однако такой код маловероятен на практике, так какisNull()редко является интересуемым свойством (сравнитеQString::isEmpty()/isNull()иQTime::isValid/isNull).QVariant::operator==используетQMetaType::equalsв Qt 6. Поэтому некоторые графические типы, такие какQPixmap,QImageиQIcon, никогда не будут сравниваться как равные. Кроме того, числа с плавающей точкой, хранящиеся вQVariant, больше не сравниваются сqFuzzyCompare, а вместо этого используют точные сравнения.
Класс QMetaType
В Qt 6 регистрация компараторов, а также операторов потоковой передачи cQDebug и QDataStream выполняется автоматически. Следовательно, QMetaType::registerEqualsComparator(), QMetaType::registerComparators(), qRegisterMetaTypeStreamOperators() и QMetaType::registerDebugStreamOperator() больше не существуют. При переносе на Qt 6 вызовы этих методов необходимо удалить.
Регистрация типов
Типы, используемые в Q_PROPERTY, хранят свой метатип в классе QMetaObject. Это требует, чтобы типы были завершены, когда moc видит их, что может привести к ошибкам компиляции в коде, который работал в Qt 5. Существует три способа решения этой проблемы:
- Включить заголовок, определяющий тип.
- Вместо включения, используйте макрос
Q_MOC_INCLUDE. Это помогает, если включение заголовка приведет к циклической зависимости или замедлит компиляцию. - Если заголовок присутствует в cpp-файле, реализующем класс, можно также включить сгенерированный moc-файл.
Классы регулярных выражений
Класс QRegularExpression
В Qt6 из нашего кода были удалены все методы, принимающие QRegExp. Поэтому, весьма вероятно, вам придётся перенести своё приложение или библиотеку на QRegularExpression.
QRegularExpression реализует Perl-совместимые регулярные выражения. Полностью поддерживает Unicode. Для обзора синтаксиса регулярных выражений, поддерживаемого QRegularExpression, обратитесь к упомянутой странице man pcrepattern(3). Регулярное выражение состоит из двух частей: строки шаблона и набора опций шаблона, которые изменяют значение строки шаблона.
Существуют некоторые тонкие различия между QRegularExpression и QRegExp, которые будут объяснены в этом документе для облегчения переноса.
QRegularExpression более строг в отношении синтаксиса регулярного выражения. Поэтому всегда полезно проверять выражение на правильность.
QRegularExpression практически всегда можно объявлять const (за исключением случаев, когда шаблон изменяется), в то время как QRegExp почти никогда не мог быть таким.
Нет замены перечислению CaretMode. Опция соответствия QRegularExpression::AnchoredMatchOption может быть использована для эмуляции поведения QRegExp::CaretAtOffset. Нет эквивалентов для других режимов QRegExp::CaretMode.
QRegularExpression поддерживает только Perl-совместимые регулярные выражения. Тем не менее, он не поддерживает все функции, доступные в Perl-совместимых регулярных выражениях. Наиболее заметным является тот факт, что дублирование имён для групп захвата не поддерживается, а их использование может привести к неопределённому поведению. Это может измениться в будущих версиях Qt.
Сопоставление по маске
Нет прямого способа выполнить сопоставление по маске в QRegularExpression. Однако, предоставлен метод QRegularExpression::wildcardToRegularExpression для преобразования шаблонов glob в Perl-совместимое регулярное выражение, которое можно использовать для этой цели.
Например, если у вас есть код:
QRegExp wildcard("*.txt");
wildcard.setPatternSyntax(QRegExp::Wildcard); можно переписать его как:
auto wildcard = QRegularExpression(QRegularExpression::wildcardToRegularExpression("*.txt")); Однако обратите внимание, что не все шаблоны масок оболочек могут быть преобразованы ожидаемым образом. Следующий пример кода молча сломается, если просто преобразовать его с помощью упомянутой выше функции:
const QString fp1("C:/Users/dummy/files/content.txt");
const QString fp2("/home/dummy/files/content.txt");
QRegExp re1("*/files/*");
re1.setPatternSyntax(QRegExp::Wildcard);
... = re1.exactMatch(fp1); // returns true
... = re1.exactMatch(fp2); // returns true
// but converted with QRegularExpression::wildcardToRegularExpression()
QRegularExpression re2(QRegularExpression::wildcardToRegularExpression("*/files/*"));
... = re2.match(fp1).hasMatch(); // returns false
... = re2.match(fp2).hasMatch(); // returns false Поиск вперёд
Поиск в строке в прямом направлении обычно реализовывался с помощью цикла с использованием QRegExp::indexIn и увеличивающегося смещения, но теперь его можно легко реализовать с помощью QRegularExpressionMatchIterator или QString::indexOf.
Например, если у вас есть код, подобный
QString subject("the quick fox");
int offset = 0;
QRegExp re("(\\w+)");
while ((offset = re.indexIn(subject, offset)) != -1) {
offset += re.matchedLength();
// ...
} вы можете переписать его как
QRegularExpression re("(\\w+)");
QString subject("the quick fox");
QRegularExpressionMatchIterator i = re.globalMatch(subject);
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
// ...
}
// or alternatively using QString::indexOf
qsizetype from = 0;
QRegularExpressionMatch match;
while ((from = subject.indexOf(re, from, &match)) != -1) {
from += match.capturedLength();
// ...
} Поиск в обратном направлении
Поиск в строке в обратном направлении обычно реализовывался как цикл по QRegExp::lastIndexIn, но теперь его можно легко реализовать, используя QString::lastIndexOf и QRegularExpressionMatch.
Примечание: QRegularExpressionMatchIterator не может выполнять поиск в обратном направлении.
Например, если у вас есть код, подобный
int offset = -1;
QString subject("Lorem ipsum dolor sit amet, consetetur sadipscing.");
QRegExp re("\\s+([ids]\\w+)");
while ((offset = re.lastIndexIn(subject, offset)) != -1) {
--offset;
// ...
} вы можете переписать его как
qsizetype from = -1;
QString subject("Lorem ipsum dolor sit amet, consetetur sadipscing.");
QRegularExpressionMatch match;
QRegularExpression re("\\s+([ids]\\w+)");
while ((from = subject.lastIndexOf(re, from, &match)) != -1) {
--from;
// ...
} exactMatch против match.hasMatch
QRegExp::exactMatch выполнял две функции: точное соответствие регулярного выражения строке-предмету и реализацию частичного соответствия. Точное соответствие указывает, соответствует ли регулярное выражение всей строке-предмету. Например:
QString source("abc123");
QRegExp("\\d+").exactMatch(source); // returns false
QRegExp("[a-z]+\\d+").exactMatch(source); // returns true
QRegularExpression("\\d+").match(source).hasMatch(); // returns true
QRegularExpression("[a-z]+\\d+").match(source).hasMatch(); // returns true Точное соответствие не отражено в QRegularExpression. Если вы хотите убедиться, что строка-предмет точно соответствует регулярному выражению, вы можете обернуть шаблон с помощью функции QRegularExpression::anchoredPattern:
QString source("abc123");
QString pattern("\\d+");
QRegularExpression(pattern).match(source).hasMatch(); // returns true
pattern = QRegularExpression::anchoredPattern(pattern);
QRegularExpression(pattern).match(source).hasMatch(); // returns false Минимальное соответствие
QRegExp::setMinimal() реализовывал минимальное соответствие, просто изменив жадность квантификаторов (QRegExp не поддерживал ленивые квантификаторы, такие как *?, +?, и т.д.). QRegularExpression вместо этого поддерживает жадные, ленивые и собственнические квантификаторы. Опция шаблона QRegularExpression::InvertedGreedinessOption может быть полезна для эмуляции эффектов QRegExp::setMinimal(): при включении она меняет жадность квантификаторов (жадные становятся ленивыми и наоборот).
Разный синтаксис шаблонов
Перевод регулярного выражения из QRegExp в QRegularExpression может потребовать изменений в самом шаблоне. Поэтому рекомендуется проверить используемый шаблон с помощью метода QRegularExpression::isValid. Это особенно важно для шаблонов, введённых пользователем или шаблонов, не контролируемых разработчиком.
В других случаях шаблон, перенесённый из QRegExp в QRegularExpression, может незаметно изменить семантику. Поэтому необходимо проверить используемые шаблоны. Наиболее заметные случаи скрытой несовместимости:
- Для использования шестнадцатеричного escape-последовательности, например,
\xHHHHс более чем 2 цифрами, нужны фигурные скобки. Шаблон, подобный\x2022необходимо переписать в\x{2022}, иначе он будет соответствовать пробелу(0x20)и строке"22". В общем случае настоятельно рекомендуется всегда использовать фигурные скобки с escape-последовательностью\x, независимо от количества указанных цифр. - Квантификация
0-to-nтипа{,n}должна быть переписана в{0,n}для сохранения семантики. В противном случае шаблон, такой как\d{,3}фактически будет соответствовать цифре, за которой следует строка"{,3}".
Частичное соответствие
При использовании QRegExp::exactMatch(), если точное соответствие не было найдено, можно узнать, сколько символов строки-предмета было сопоставлено с регулярным выражением, вызвав QRegExp::matchedLength(). Если возвращаемая длина равна длине строки-предмета, можно заключить, что было найдено частичное соответствие. QRegularExpression явно поддерживает частичное соответствие с помощью соответствующего QRegularExpression::MatchType.
Глобальное соответствие
Из-за ограничений API QRegExp невозможно правильно реализовать глобальное соответствие (как в Perl). В частности, шаблоны, которые могут соответствовать нулю символов (например, "a*"), представляют проблему. QRegularExpression::wildcardToRegularExpression правильно реализует глобальное соответствие Perl, и возвращаемый итератор можно использовать для проверки каждого результата.
Поддержка свойств Юникода
При использовании QRegExp, классы символов, такие как \w, \d, и т.д., сопоставляют символы с соответствующим свойством Юникода: например, \d соответствует любому символу со свойством Юникода Nd (десятичная цифра). По умолчанию эти классы символов сопоставляют только символы ASCII. При использовании QRegularExpression: например, \d соответствует ровно символу в диапазоне ASCII 0-9. Это поведение можно изменить, используя опцию шаблона QRegularExpression::UseUnicodePropertiesOption.
Класс QRegExp
В Qt6 класс QRegExp был удалён из Qt Core. Если ваше приложение не может быть перенесено прямо сейчас, QRegExp всё ещё существует в Qt5Compat, чтобы эти базы кода работали. Если вы хотите использовать QRegExp дальше, вам нужно связаться с новым модулем Qt5Compat и добавить эту строку в ваш файл qmake .pro:
QT += core5compat
В случае, если вы уже портировали ваше приложение или библиотеку в систему сборки cmake, добавьте следующее в ваш CMakeList.txt:
PUBLIC_LIBRARIES
Qt::Core5Compat QEvent и подклассы
Класс QEvent определяет конструктор копирования и оператор присваивания, несмотря на то, что он является полиморфным классом. Копирование классов с виртуальными методами может привести к «обрезанию» (slicing), когда объекты разных классов присваиваются друг другу. Поскольку копирование и присваивание часто происходит неявно, это может привести к трудноотлаживаемым проблемам.
В Qt 6 конструктор копирования и оператор присваивания для подклассов QEvent были сделаны защищёнными, чтобы предотвратить неявное копирование. Если вам нужно скопировать события, используйте метод clone, который вернёт копию объекта QEvent с выделением памяти. Убедитесь, что вы удаляете клон, возможно, используя std::unique_ptr, если вы не помещаете его в очередь (в этом случае Qt удалит его после доставки).
В своих подклассах QEvent переопределите clone() и объявите защищённый конструктор копирования и оператор присваивания по умолчанию следующим образом:
class MyEvent : public QEvent
{
public:
// ...
MyEvent *clone() const override { return new MyEvent(*this); }
protected:
MyEvent(const MyEvent &other) = default;
MyEvent &operator=(const MyEvent &other) = default;
MyEvent(MyEvent &&) = delete;
MyEvent &operator=(MyEvent &&) = delete;
// member data
}; Обратите внимание, что если ваш класс MyEvent выделяет память (например, с помощью шаблона указатель на реализацию), вам придётся реализовать пользовательскую семантику копирования.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/qtcore-changes-qt6.html