Russian Qt Forum

Qt => Общие вопросы => Тема начата: Alex Custov от Июль 19, 2018, 20:41



Название: UB или нет?
Отправлено: Alex Custov от Июль 19, 2018, 20:41
Код QString:

Код
C++ (Qt)
const ushort *QString::utf16() const
{
   if (IS_RAW_DATA(d)) {
       // ensure '\0'-termination for ::fromRawData strings
       const_cast<QString*>(this)->reallocData(uint(d->size) + 1u);
   }
   return d->data();
}

См. https://code.woboq.org/qt5/qtbase/src/corelib/tools/qstring.cpp.html#_ZN7QString11reallocDataEjb

reallocData() модифицирует член класса d. А что если объект QString - константный? Модификация константного объекта или его членов с использованием const_cast<T*> - UB. Так я всегда думал... Или нет?


Название: Re: UB или нет?
Отправлено: Пантер от Июль 19, 2018, 20:50
Нет, это не UB.


Название: Re: UB или нет?
Отправлено: Alex Custov от Июль 19, 2018, 20:55
Нет, это не UB.

Спасибо за ответ :D Почему?


Название: Re: UB или нет?
Отправлено: Пантер от Июль 19, 2018, 21:03
Ты можешь спокойно снимать константность, никто тебя за это не накажет. Кроме другого разработчика, конечно. :). А есть еще mutable мемберы.


Название: Re: UB или нет?
Отправлено: Авварон от Июль 19, 2018, 21:07
А если строев в RO памяти?


Название: Re: UB или нет?
Отправлено: Alex Custov от Июль 19, 2018, 21:09
Ты можешь спокойно снимать константность, никто тебя за это не накажет. Кроме другого разработчика, конечно. :). А есть еще mutable мемберы.

член d - не mutable. https://en.cppreference.com/w/cpp/language/const_cast говорит:

Цитировать
const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object or a reference or pointer to non-volatile type that is actually referring to a volatile object. Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

Код
C++ (Qt)
struct type {
   int i;
 
   type(): i(3) {}
 
   void f(int v) const {
       // this->i = v;                 // compile error: this is a pointer to const
       const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
   }
};
 
type t; // if this was const type t, then t.f(4) would be undefined behavior
t.f(4);
 


Название: Re: UB или нет?
Отправлено: kuzulis от Июль 19, 2018, 22:59
Ну, в Qt это нормальная практика кастить приватные мемберы к const_cast в константных методах.

Модифицируется то не QString, а d-пойнтер! А он вроде не константный.


Название: Re: UB или нет?
Отправлено: Alex Custov от Июль 19, 2018, 23:19
Модифицируется то не QString, а d-пойнтер! А он вроде не константный.

d-pointer - это мембер класса. Компилятор засунет весь константный объект QString в RO секцию - и что ты там будешь модифицировать? См. мой предыдущий комент с выдержками из cppreference.


Название: Re: UB или нет?
Отправлено: Igors от Июль 20, 2018, 09:18
reallocData() модифицирует член класса d. А что если объект QString - константный? Модификация константного объекта или его членов с использованием const_cast<T*> - UB. Так я всегда думал... Или нет?
Ну хорошо, допустим мы "не писяем и не какаем" (все только легально) - а что делать в данном случае? Метод utf16() должен возвращать данные с нуль-терминатором - ну вот "так надо", но "сырая" строка его не имеет. Давайте utf16() делать НЕ константным! (др предложений никогда не слыхал). Ну так это еще хуже, вся константность развалится по цепочке.

Кстати не вижу случая когда IS_RAW_DATA сработает. Методы setRawData/fromRawData (вроде бы позволяют строке ссылаться на внешние данные) почему-то упорно их копируют


Название: Re: UB или нет?
Отправлено: Авварон от Июль 20, 2018, 11:34
Да, подумав ещё, такая строка не может лежать в ро-секции, чтобы её инициализировать, надо вызвать fromRawData, который не constexpr, а значит будет вызван в рнтайме, а значит аллоцирует контрольный блок и запишет в d. Т.е. этот d обязан лежать в rw памяти.


Название: Re: UB или нет?
Отправлено: Alex Custov от Июль 20, 2018, 12:34
Да, подумав ещё, такая строка не может лежать в ро-секции, чтобы её инициализировать, надо вызвать fromRawData, который не constexpr, а значит будет вызван в рнтайме, а значит аллоцирует контрольный блок и запишет в d. Т.е. этот d обязан лежать в rw памяти.

Почему?

Код
C++ (Qt)
const QString str = QString::fromRawData(unicode, size);

Разве str за кулисами будет лежать в rw?


Название: Re: UB или нет?
Отправлено: Авварон от Июль 20, 2018, 14:20
Почему?

Код
C++ (Qt)
const QString str = QString::fromRawData(unicode, size);

Разве str за кулисами будет лежать в rw?

Да, конструктор нетривиальный нужен.
Всё, что не может быть сделано на этапе компиляции, делается в рантайме. В данном случае при загрузке бинаря вызывается функция fromRawData, аллоцируется контрольный блок, который надо записать в d.
Это возможно только если d не в RO.


Название: Re: UB или нет?
Отправлено: Igors от Июль 21, 2018, 09:24
А откуда эта легенда про "RO"?  Насколько я понял Read Only (Memory). И зачем так упорно хвататься за этот частный случай? Отловить дефекты const_cast можно напр так
Код
C++ (Qt)
static const QChar data[4] = { 0x0055, 0x006e, 0x10e3, 0x03a3 };
QString str = QString::fromRawData(data, 4);
const QChar * p0 = str.constData();
const ushort * utf = str.utf16();
const QChar * p1 = str.constData();
qDebug() << data << p0 << p1 << (p0 == p1);
 
Причем константный data() (вместо constData()) не катит, он также скопирует, причем даже не прибегая к const_cast, а с наглой мордой: reinterpret_cast

Поэтому формально да, UB. А по сути, ф-ционалу - верное решение. Хотели использовать "внешние" данные - получите, пусть и с геморроем


Название: Re: UB или нет?
Отправлено: Авварон от Июль 21, 2018, 21:31
А откуда эта легенда про "RO"?  Насколько я понял Read Only (Memory). И зачем так упорно хвататься за этот частный случай? Отловить дефекты const_cast можно напр так
Код
C++ (Qt)
static const QChar data[4] = { 0x0055, 0x006e, 0x10e3, 0x03a3 };
QString str = QString::fromRawData(data, 4);
const QChar * p0 = str.constData();
const ushort * utf = str.utf16();
const QChar * p1 = str.constData();
qDebug() << data << p0 << p1 << (p0 == p1);
 
Причем константный data() (вместо constData()) не катит, он также скопирует, причем даже не прибегая к const_cast, а с наглой мордой: reinterpret_cast

Поэтому формально да, UB. А по сути, ф-ционалу - верное решение. Хотели использовать "внешние" данные - получите, пусть и с геморроем

Вы в курсе, что такое UB вообще? В данном случае UB нет, поведение странное, но четко определенное. Вот если бы при вызове utf16 ваша кошка рожала котенка - это было бы UB.


Название: Re: UB или нет?
Отправлено: Igors от Июль 22, 2018, 05:59
Вы в курсе, что такое UB вообще? В данном случае UB нет, поведение странное, но четко определенное. Вот если бы при вызове utf16 ваша кошка рожала котенка - это было бы UB.
Да, если понимать UB буквально (undefined). Ну ТС трактует ширше, типа "некорректно" (с точки зрения  языка), почему бы и нет? В данном случае так и есть, никакое оно не "странное", а просто-напросто неверное, напр использующий p0 рухнет после вызова utf16 (объявленного как константный).

Кстати не очень понял а зачем копировать "внешние" данные при первом же шорохе, какая в этом необходимость? Не дороговато ли обходится "завершающий ноль" для QString::utf16. QString::data и др? Почему не прожить без него, size() есть


Название: Re: UB или нет?
Отправлено: Авварон от Июль 22, 2018, 12:57
Потому что это аналог c_str() у студ_строки, он обязан быть нультерминированным.
Если ноль не важен, можно юзать data()
Другое дело, что в qt6 надо бы убрать fromRawData, ведь теперь есть QStringView