C ++ строгое правило алиасинга для структуры битовых полей - PullRequest
0 голосов
/ 17 мая 2018

Не нарушает ли приведенная ниже функция-член getValue () правило строгого псевдонима в c ++?

Согласно стандарту, я полагаю, что setValue () нарушает правило строгого алиасинга, поскольку double не является ни агрегатным типом, ни базовым классом IEEE754_64.

А как насчет getValue ()? Это неопределенное поведение, когда элемент данных находится в форме битового поля, как в примере ниже?

Я использую подобный код в большом проекте. GCC -O2 и -O3 выдают неправильное значение. И проблема исчезнет, ​​если я добавлю -fno-strict-aliasing. Кроме того, проблема исчезнет, ​​если я использую memcpy вместо приведения в getValue (). Не уверен, что это ошибка GCC.

#include <iostream>
#include <cstring>

using namespace std;
struct IEEE754_64
{
  void setValue(double);
  unsigned long long getValue();
  // Data members
  unsigned long long d_mantissa : 52;
  long long d_exponent : 11;
  unsigned long long d_sign : 1;
};

void IEEE754_64::setValue(double d)
{
  (*this) = *reinterpret_cast<IEEE754_64*>(&d);
}

unsigned long long IEEE754_64::getValue()
{
  return * reinterpret_cast<unsigned long long *>(this);
}

int main()
{
  double b = 1.0;
  IEEE754_64 d;

  memcpy(&d, &b, sizeof(double));
  cout<<hex<<d.getValue()<<endl;

  d.setValue(1.0);
  cout<<hex<<d.getValue()<<endl;
  return 0;
}

1 Ответ

0 голосов
/ 17 мая 2018

Поведение reinterpret_cast<T *>(&a) зависит от того, какой объект на самом деле находится в ячейке памяти a.

Неформально, если там действительно есть объект T (что, конечно, может произойти, только если T является подобъектом a или наоборот), тогда результат приведенияуказатель на объект T

В противном случае результатом приведения является указатель на a с неверным типом, и чтение его может нарушить правило строгого наложения имен.

Формально вышеизложенное объясняется в Стандарте в разделах [expr.static.cast] / 13 и [basic.compound] / 4, см. Здесь для получения подробной информации.


Имея это в виду, setValue читает double через lvalue типа IEEE754_64, нет никаких сомнений, что это строгое нарушение псевдонимов.

Для getValue В этом случае мы должны понимать поведение reinterpret_cast<unsigned long long *>(this), которое менее прямолинейно.

Согласно [basic.compound] / 4, объект и его первый нестатический член данных всегда взаимно конвертируемы по указателю.,Это не перечисляет никаких исключений для битовых полей.

Но соответствующий текст из [expr.static.cast] / 13:

В противном случае, если исходное значение указателя указывает на объект a, и существуетобъект b типа T (игнорируя квалификацию cv), который можно преобразовать в указатель с a, результатом будет указатель на b.

Если мы примем этот бит-field это «объект типа unsigned long long», из чего следует, что результатом приведения является указатель на битовое поле.Однако стандарт не определяет поведение указателей на битовые поля.

Итак, IMHO, лучший способ интерпретации приведенного выше текста - сказать, что битовое поле не является объектом типа unsigned long long.Я считаю, что это соответствует остальной части стандарта;даже если нет значений типа битового поля, он определенно говорит о значениях типа битового поля.

Подведение итогов;Я считаю, что результат reinterpret_cast<unsigned long long *>(this) НЕ является указателем на this->d_mantissa, поэтому функция getValue() получает доступ к объекту типа IEEE754_64, используя glvalue типа unsigned long long, нарушая правило строгого алиасинга.

...