自定义可选打破严格的别名规则

custom optional breaks strict aliasing rules

我写了一个自定义可选 class(因为我被迫使用没有 STL 的 C++98)。 它看起来像这样:

template <typename T>
struct optional {
    char value[sizeof(T)];
    bool has_value;

    T& operator*() {
        return *reinterpret_cast<T*>(value);
    }
};

编译器产生警告 dereferencing type-punned pointer will break strict aliasing rules

我该怎么做才能在没有 UB 的情况下制作这个 class? 也许应该使用 memcpy,但我不明白如何使用。

避免警告的一种方法是使用类型区分 uinion。不幸的是,在 C++98 中,它只适用于具有默认构造函数的类型。

即:

...
union {
   char empty;
   T value;
} storage;
bool has_value;
...

// when object is set to value
new (&storage.value) T(/* arg *);
has_value = true;

...
T& operator() {
   // check for has_value should be here
   return storage.value;
...

~optiona() {
   if (has_value) storage.value.~T();
...

这是有效的,因为当创建联合时,第一个元素被构造(空字符)。当您将值放入可选值时,您激活了联合的第二个成员(值),并且它被明确定义为可以访问它。

What can I do to make this class without UB?

  • 使用 placement-new 创建对象。
  • optional
  • 的析构函数中调用创建对象的析构函数
  • 不要重新解释存储地址。使用从 placement new 返回的指针。不幸的是,这意味着您需要将指针存储为成员。您可以替换布尔值,因为空指针表示空状态。
  • 注意对齐。在 C++11 之前,这可能非常棘手。您可能需要依赖非标准语言功能来实现此目的。 给定一个具有非常严格对齐的指针成员,并且 C++98 没有过度对齐的类型这一事实,您可能会忽略对齐对于大多数类型。

动态分配对象会容易得多。更慢,很有可能。但更简单且符合标准。