Совместим ли этот союз со строгими правилами псевдонимов?

Можно ли использовать обе части объединения, если вы знаете, что части не пересекаются? Как и в этом примере, можно ли использовать как buf[31], так и ps?

struct PtrSize {
  const char *data;
  size_t size;
};

class SmallStringOrNot {
  union {
    PtrSize  ps;
    char     buf[32];
  } pb;

public:
  bool IsSmallString() const {
    return pb.buf[31] != 0;
  }
  SmallStringOrNot(const char *str) {
    size_t len = strlen(str);
    if (len && len < 31) {
      memcpy(pb.buf, str, len);
      pb.buf[31] = len;
    } else {
      pb.ps.data = str;
      pb.ps.size = len;
      pb.buf[31] = 0;    // is this OK, accessing buf right after ps?
    }
  }
  PtrSize AsPtrSize() const {
    if (IsSmallString()) {
      return PtrSize{pb.buf, pb.buf[31]};
    } else {
      return pb.ps;
    }
  }
};

person jorgbrown    schedule 24.04.2015    source источник


Ответы (1)


К сожалению, код не в порядке: вы, по крайней мере, не находитесь в зоне «неопределенного поведения», поскольку в C++ всегда разрешен доступ к union через член char, но у вас нет гарантии, что, изменяя buf[31], вы не изменяете ps.data или ps.size. На 128-битной машине вы почти наверняка это сделаете.

На более обычных архитектурах ваш код должен быть в порядке, но для 100% гарантии вам следует обратиться к документации компилятора, поскольку size_t в принципе может быть больше, чем void*. Например, даже на 64-битной машине у вас теоретически может быть 192-битный элемент ps.size (который в сумме с 64-битным указателем ps.data приведет к тому, что PtrSize полностью перекроет буфер.

person Alberto M    schedule 24.04.2015
comment
Хммм, хорошо, если я добавлю подтверждение времени компиляции о том, что размер buf больше, чем размер PtrSize, я буду в порядке? - person jorgbrown; 25.04.2015