Изменения в 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 хранить более чем 2^32 элементов на 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 хранить более чем 2^31 элемент на 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 предоставляет унифицированное представление 16-битных строк 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;
} 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 в процессе приостановки или уже находится в приостановленном состоянии. Обратите внимание, что для обеспечения согласованности интерфейсы, связанные с приостановкой, были устаревшими и заменены аналогичными методами, содержащими «suspend» в названии. -
QFuture::waitForFinished() теперь будет ожидать, пока QFuture действительно перейдет в состояние завершения, вместо выхода сразу же, как только оно больше не находится в состоянии выполнения. Это предотвращает выход
waitForFinished()немедленно, если в момент вызова будущее еще не запущено. То же самое относится к QFutureWatcher::waitForFinished(). Это изменение не повлияет на поведение кода, который использовал QFuture с QtConcurrent. Может быть затронут только код, который использовал его с недокументированнымQFutureInterface.
QPromise
В Qt 6 для использования в качестве «заполняющего» аналога QFuture следует использовать новый класс QPromise, а не неофициальный QFutureInterface.
Классы ввода-вывода
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, обратитесь к упомянутой странице справки pcrepattern(3). Регулярное выражение состоит из двух частей: строки шаблона и набора опций шаблона, которые изменяют смысл строки шаблона.
Существуют некоторые тонкие различия между QRegularExpression и QRegExp, которые будут объяснены в этом документе, чтобы облегчить процесс переноса.
QRegularExpression более строго относится к синтаксису регулярного выражения. Поэтому всегда полезно проверять выражение на действительность.
QRegularExpression почти всегда можно объявить константным (кроме случаев, когда шаблон изменяется), в то время как 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, и возвращённый итератор можно использовать для проверки каждого результата.
Поддержка свойств Unicode
При использовании QRegExp, классы символов, такие как \w, \d, и т. д., соответствуют символам с соответствующим свойством Unicode: например, \d соответствует любому символу со свойством Unicode 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 определяет конструктор копирования и оператор присваивания, несмотря на то, что он является полиморфным классом. Копирование классов с виртуальными методами может привести к усечению при присваивании объектов разных классов друг другу. Поскольку копирование и присваивание часто происходит неявно, это может привести к трудноотлаживаемым проблемам.
В 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.0/qtcore-changes-qt6.html