Russian Qt Forum
Июль 22, 2024, 14:05 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4 5 6   Вниз
  Печать  
Автор Тема: Итераторы  (Прочитано 31183 раз)
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #30 : Апрель 20, 2021, 22:35 »

Цитировать
Там внутри могут быть всякие traits которые для разных типов разные.
Но в любом случае, даже если битовое представление одинаковое, это UB.
Могут, но это лишено практического смысла. Для контейнера важен только размер элемента (точнее, скажем "семантический размер", как в случае с bool), а сам элемнт, будь хоть POD, хоть класс с VMT - в плане хранения - все равно. Если обратное, то это уже не контейнер, а контейнер + еще что-то (т.е. bad design).  

Цитировать
В чем проблема-то? Единственный валидный юзкейз reinterpret_cast между разными типами это каст к массиву байт (или void*) и обратно.
Компилятор знает что вон тот char buffer[] потенциально может алиаситься с вот этим указателем и сгенерит менее производительный код.
placement new как раз подпадает под это - буфер чаров может алиаситься (и делает это) с указателем на произвольный T.
А вот любые другие типы (например int* в double*) алиаситься не могут
Я к тому, что поинтер-алиасинг это не только про reinterpret_cast. Вот этот код дает UB (печатает -1 вместо ожидаемого 0) в обоих случаях (MinGW 8.3, O2):
Код:
#include <iostream>

int foo(float* f, int* i)
{
*i = -1;
*f = 0.0f;

return *i;
}

int main(int, char *[])
{
int i;
//i = foo(reinterpret_cast<float*>(&i), &i);  // 1
i = foo(new(&i) float, &i);                   // 2
std::cout << i << std::endl;
}

А теперь вернусь в контекст нашей задачи - слегка модифицирую код на манер std::reference_wrapper, для упрощения я использовал просто структуру:
Код:
#include <iostream>

#if 0  // 0 - OK, 1 - UB
typedef float F;
#else
struct F
{
int i;
F(float v = 0.0f) { reinterpret_cast<float&>(i) = v; }
};
#endif

int foo(F* f, int* i)
{
*i = -1;
*f = 0.0f;

return *i;
}

int main(int, char *[])
{
int i;
//i = foo(reinterpret_cast<F*>(&i), &i);  // 1
i = foo(new(&i) F(), &i);                   // 2
std::cout << i << std::endl;
}
При #if 0 UB больше нет в обоих случаях. #if 1 - это как в предыдущем случае с UB.

Почему больше нет UB? - сходу не найду - но еще вроде со всремен C++11 правил алиасинга если один из типов агрегатный и включает другой членом - то это не UB.

Итого:
Типы reference_wrapper<T> и Т* должны быть безопасны в плане reinerpret_cast, т.к. первый инкапсулирует второй как член, что следует из того, что sizeof(reference_wrapper<T>) == sizeof(Т*)т.е. reference_wrapper<T> никак не может быть реализован по-другому, кроме как инкапсулировать указатель и больше ничего.
« Последнее редактирование: Апрель 20, 2021, 22:57 от AkonResumed » Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #31 : Апрель 20, 2021, 23:00 »

От-т! Опять я упустил внешний контейнер (QSet).

Авварон, по алиасингу вынужден с вами также согласиться. Друзья, спасибо!
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #32 : Апрель 21, 2021, 10:12 »

Код:
*i = -1;
*f = 0.0f;
Ну вообще-то компилятор имеет право выполнять эти операции в любом порядке, поэтому если адрес перекрывается, то рез-т не определен, тонкости ре-интерпретации здесь ни при чем.

Пример vector<bool> приводится так часто/неизменно, что скорее говорит о том что никаких других просто нет  Улыбающийся

С placement new как-то не уловил, неплохо бы пожевать. Спасибо

От-т! Опять я упустил внешний контейнер (QSet).
Типа "а вот возможен такой контейнер что будет иметь какие-нить traits - и тогда заклинит"? Может и да, но лично меня это совершенно не волнует. Я не собираюсь лепить врапперы всегда и везде, речь идет о конкретном случае в котором он весьма кстати. А объявить любой reinterpret_cast UB/хаком никакого ума не требует. Заметьте что чем больше человек говорит о корректности - тем меньше решений предлагает. Такой подход не конструктивен.

Цитата: AkonResumed link=topic=32938.msg244690#msg244690
Авварон, по алиасингу вынужден с вами также согласиться. Друзья, спасибо!
Возможно/предполагаю, Вы почувствовали что разбор этих тонкостей (или толстостей) отнимает слишком много времени, а работа-то стоит. Да, согласен Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #33 : Апрель 21, 2021, 10:21 »

Может и да, но лично меня это совершенно не волнует. Я не собираюсь лепить врапперы всегда и везде, речь идет о конкретном случае в котором он весьма кстати.
Да вы можете это брать и использовать. Улыбающийся
Дальнейшее обсуждения больше для разработчиков, что бы никто из них по неосторожности у себя не задействовал.
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #34 : Апрель 21, 2021, 10:36 »

Цитировать
Вот этот код дает UB (печатает -1 вместо ожидаемого 0)

Почему UB? Он и должен выводить -1.
В коде же явно написано "*i = -1", а i передаётся поинтером 1 в 1.

Другое дело, что такой код в продакшене никто использовать не будет.
Разве что как тест для компилятора.


Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #35 : Апрель 21, 2021, 10:53 »

UB, потому что следом *f = 0.0f; зануляет туже самую область памяти. Так и происходит, но компилятор, при возврате в return *i; попросту не перезагружает значение из памяти, потому как считает, что f не может указывать на ту же ячеку, что и i, соответственно, она не может быть изменена через f. Для return *i; компилятор просто хардкодит 1 в eax/rax (регистр, через который идет возврат значения функции).
Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #36 : Апрель 21, 2021, 11:02 »

Да, и порядок выполнения операций здесь не при чем. Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык. UB возникает именно из-за оптимизатора и формального правила стандарта, которое он использует для оптимизации.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #37 : Апрель 21, 2021, 11:23 »

Да, и порядок выполнения операций здесь не при чем. Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык. UB возникает именно из-за оптимизатора и формального правила стандарта, которое он использует для оптимизации.
Не уверен. Вот был интересный случай
Код
C++ (Qt)
float Rand( uint32 & seed );
 
QVector3D vec(Rand(seed), Rand(seed), Rand(seed));
Эта ошибка долго оставалась незамеченной, но когда перешел на шланг - тот выдал варнинт, молодец.

Конечно в в любом случае перекрывать адреса = ненужный поиск приключений. Вообще употребляемые здесь слова "продакшн", "разработка" и.т.п. - это просто "надувание щек", здесь обсуждается всего лишь мелкая деталь  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #38 : Апрель 21, 2021, 11:28 »

Вообще употребляемые здесь слова "продакшн", "разработка" и.т.п. - это просто "надувание щек", здесь обсуждается всего лишь мелкая деталь  Улыбающийся
Боже, от вас слышать про надувание щек... Улыбающийся
Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #39 : Апрель 21, 2021, 11:29 »

И как вы предполагали - какой Rand будет вызван первым - левый или правый?
Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #40 : Апрель 21, 2021, 11:41 »

В этом контексте (порядка вычисления аргументов) частая ошибка, допускающая лик:
Код:
void foo(std::shared_ptr<MyClass> c, int x);

foo(std::shared_ptr<MyClass>(new MyClass), getXValue());

Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #41 : Апрель 21, 2021, 11:47 »

Вот этот код дает UB (печатает -1 вместо ожидаемого 0) в обоих случаях (MinGW 8.3, O2):

Да, прикольно, не задумывался о том что placement new можно звать на unrelated типах.
Небось тоже где-то прописано что так делать нельзя (кроме char*/void*)
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #42 : Апрель 21, 2021, 11:52 »

И как вы предполагали - какой Rand будет вызван первым - левый или правый?

В 2ГИСе мы спрашивали похожую задачку
std::cout << f() << f() << f() << std::endl;

где f() имеет сайд-эффект (делает i++)

никто не решил (я в том числе)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #43 : Апрель 21, 2021, 11:58 »

И как вы предполагали - какой Rand будет вызван первым - левый или правый?
Мне все равно (будет надо - посмотрю в отладчике), главное рез-т должен быть воспроизводимым при том же seed.

Порядок выполнения не нарушет видимый (ожидаемый) результат однопоточного исполнения. Это же императивный язык.
Это противоречит духу/идее "конвейера". Во всяком случае "распаковкой операндов" для последующих команд(ы) процессор занимается.

никто не решил (я в том числе)
Любая задача имеет по меньшей мере одно решение: снятие самой задачи/проблемы. Здесь оно и будет лучшим - не нужны такие заморочки.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #44 : Апрель 21, 2021, 12:03 »

Любая задача имеет по меньшей мере одно решение: снятие самой задачи/проблемы. Здесь оно и будет лучшим - не нужны такие заморочки.

Ну там задачи были разной сложности что позволяло оценить уровень кандидата. Полезно иметь задачу с подвохом ультимативной сложности. Если человек скажет какой-то один из возможных вариантов, то норм (там еще надо продраться сквозь код же), а если про точки следования вспомнит - ваще бох. Правда на моей памяти никто без подсказок не вспомнил, но это и не особо влияло.
Записан
Страниц: 1 2 [3] 4 5 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.08 секунд. Запросов: 23.