С++ строгий независимый алиасинг

Я читал много QA о строгом псевдониме здесь, в Stack Overflow, но все они довольно распространены, и обсуждение всегда имеет тенденцию ссылаться на глубокие детали стандарта C ++, которые почти всегда трудно понять правильно. Особенно, когда стандартные вещи не говорят прямо, а описывают что-то мутно и непонятно. Итак, мой вопрос, вероятно, является возможным дублированием тонн QA здесь, но, пожалуйста, просто ответьте на конкретный вопрос:

Это правильный способ сделать "nonalias_cast"?:

template<class OUT, class IN>
inline auto nonalias_cast(IN *data) {
    char *tmp = reinterpret_cast<char *>(data);
    return reinterpret_cast<OUT>(tmp);
}

float f = 3.14;
unsigned *u = nonalias_cast<unsigned *>(&f);
*u = 0x3f800000;
// now f should be equal 1.0

Думаю, ответ нет. Но есть ли хороший обходной путь? Кроме отключения флага строгого сглаживания, конечно. Union также не является удобным вариантом, если только нет способа поместить хак union в тело функции nonalias_cast. memcpy здесь тоже не вариант - изменение данных должно быть синхронизировано.

Несбыточная мечта или ускользающая реальность?

ОБНОВЛЕНИЕ:

Хорошо, поскольку у нас есть отрицательный ответ на вопрос "возможно ли это?" вопрос, я хотел бы задать вам дополнительный вопрос, который меня беспокоит:

Как бы вы решили эту задачу? Я имею в виду, что есть масса практических задач, которые более-менее требуют подхода "поиграй с битами". Например, предположим, что вам нужно написать преобразователь с плавающей запятой IEEE-754, например этот . Меня больше волнует практическая сторона вопроса: как найти обходной путь для достижения цели? По крайней мере, "боль в @#$".


person Nikolai Shalakin    schedule 09.10.2017    source источник
comment
под приведением без псевдонимов вы подразумеваете приведение, которое не нарушает строгих правил псевдонимов?   -  person bolov    schedule 09.10.2017
comment
Дело не в том, что Стандарт запрещает reinterpret_casts изменять тип объекта. Скорее, существует только один тип объекта и очень ограниченное число других типов, которые можно использовать для его чтения. Вы не можете читать из объекта float выражение, утверждающее, что этот объект является unsigned (не уверен в формировании этого указателя, он может быть смещен).   -  person dyp    schedule 09.10.2017
comment
Относительно ваших union идей: это также не может работать, так как UB читать union члена, отличного от того, который был назначен последним.   -  person Baum mit Augen    schedule 09.10.2017
comment
@BaummitAugen, я знаю это. Но я также знаю, что это распространенный обходной путь, и почти любой компилятор будет работать так, как ожидалось. Несмотря на то, что это противоречит стандарту.   -  person Nikolai Shalakin    schedule 09.10.2017
comment
Там бы, например. должен быть атрибутом объекта (и атрибутом указателя), сообщающим компилятору, что объект rsp. указатель может подвергаться алиасингу. И это должно было бы быть частью правильной системы типов, если бы внешние функции также были разрешены. Это было бы а) нестандартно и б) много усилий для такого второстепенного случая.   -  person Arne Vogel    schedule 09.10.2017
comment
@Oliv Ну да, вы можете иметь несколько объектов по одному и тому же адресу; по крайней мере, тип объекта complete уникален. Но вам разрешено читать значение объекта с несколькими типами, отличными от типа этого объекта, такими как типы символов, версии типа с указанием cv и т. д.   -  person dyp    schedule 10.10.2017
comment
@dyp Извините, я снова прочитал ваш первый комментарий, кажется, я понял, что вы говорили противоположное тому, что вы на самом деле написали, мой комментарий как будто пытается убедить вас, что вы правы, начав с нет .... !!   -  person Oliv    schedule 10.10.2017
comment
Относительно IEEE-754 FP см. Как реализовать быстрый обратный sqrt без неопределенного поведения?. Короткий ответ оттуда: используйте memcpy.   -  person Ruslan    schedule 12.03.2018


Ответы (3)


Как правильно указали другие ответы: это невозможно, поскольку вам не разрешен доступ к объекту float через указатель unsigned, и нет приведения, которое удалит это правило.

Итак, как вы решаете эту проблему? Не обращайтесь к объекту через указатель unsigned! Используйте float* или char* для передачи объекта, так как это единственные типы указателей, которые разрешены при строгом псевдониме. Затем, когда вам действительно нужно получить доступ к объекту в семантике unsigned, вы выполняете memcpy из float* в локальный unsignedmemcpy обратно, когда закончите). Ваш компилятор будет достаточно умен, чтобы сгенерировать для этого эффективный код.

Обратите внимание, что это означает, что у вас будет float* везде на ваших интерфейсах вместо unsigned*. И это именно то, что заставляет это работать: система типов всегда знает о правильных типах данных. Все начинает рушиться, только если вы попытаетесь протащить float через систему типов как unsigned*, что, как вы, надеюсь, согласитесь, в первую очередь является подозрительной идеей.

person ComicSansMS    schedule 09.10.2017
comment
Спасибо, действительно хороший пример. Memcpy с нулевой стоимостью впечатляет. - person Nikolai Shalakin; 09.10.2017

Это правильный способ сделать «nonalias_cast»?

No.

Но есть ли хороший обходной путь?

Опять же, нет.

Причина обоих заключается просто в том, что &f не является адресом какого-либо объекта типа unsigned int, и никакое приведение к указателю не изменит этого.

person Baum mit Augen    schedule 09.10.2017

Нет, ваш nonalias_cast не работает и не может работать.

Правила псевдонимов типов (непосредственно) не связаны с преобразованием указателей. На самом деле ни одно из ваших преобразований не имеет неопределенного поведения. Правила касаются доступа к объекту определенного типа через указатель другого типа.

Независимо от того, как вы конвертируете указатель, указанный объект по-прежнему является объектом float, и доступ к нему через указатель unsigned нарушает правила псевдонимов типов.


Несбыточная мечта или ускользающая реальность?

В стандартном C++ это невозможно.

person eerorika    schedule 09.10.2017