C++14标准布局类型是否可以对字段使用“alignas”

Can C++14 standard-layout types use `alignas` for fields?

本文关键字:字段 alignas 布局 标准 类型 是否 C++14      更新时间:2023-10-16

我想使用模板来简化具有非平凡类型的并集的构造。以下内容在实践中似乎"有效",但在技术上不符合规范:

template<typename T> struct union_entry {
  void (*destructor_)(void *);  // how to destroy type T when active
  T value_;
};
union U {
  union_entry<A> a;
  union_entry<B> b;
  // ... some constructor and destructor...
};

问题是(根据N4141(,只有当两个结构都是标准布局类型时,才能访问并集中两个结构的公共初始序列(即destructor_字段(——至少根据9.5.1中的非规范性注释。根据9.0.7,标准布局类型不能有任何具有非标准布局的非静态数据成员。因此,如果A或B不是标准布局,那么在错误的并集中访问destructor_就是非法的。

一个漏洞似乎是通过将value_转换为alignas(T) char[sizeof(T)]来制作union_entry标准布局。9.0.7中的任何内容似乎都不排除使用alignas。因此,我的问题是:以下是任何类型T的标准布局类型吗因此,是否可以将value_强制转换为T&以模拟前面的示例,同时仍然允许在非活动union_entry中使用destructor_

template<typename T> struct union_entry {
  void (*destructor_)(void *);
  alignas(T) char value_[sizeof(T)];
}

在clang-3.8.1和g++6.2.1中,std::is_standard_layout建议union_entry<T>是标准布局,即使T不是。以下是我想如何使用这种技术的完整工作示例:

#include <cassert>
#include <iostream>
#include <new>
#include <string>
using namespace std;
template<typename T> struct union_entry {
  void (*destructor_)(void *);
  alignas(T) char value_[sizeof(T)];
  union_entry() : destructor_(nullptr) {}
  ~union_entry() {}   // Just to cause error in unions w/o destructors
  void select() {
    if (destructor_)
      destructor_(this);
    destructor_ = destroy_helper;
    new (static_cast<void *>(value_)) T{};
  }
  T &get() {
    assert(destructor_ == destroy_helper);
    return *reinterpret_cast<T *>(value_);
  }
private:
  static void destroy_helper(void *_p) {
    union_entry *p = static_cast<union_entry *>(_p);
    p->get().~T();
    p->destructor_ = nullptr;
  }
};
union U {
  union_entry<int> i;
  union_entry<string> s;
  U() : i() {}
  ~U() { if (i.destructor_) i.destructor_(this); }
};
int
main()
{
  U u;
  u.i.select();
  u.i.get() = 5;
  cout << u.i.get() << endl;
  u.s.select();
  u.s.get() = "hello";
  cout << u.s.get() << endl;
  // Notice that the string in u.s is destroyed by calling
  // u.i.destructor_, not u.s.destructor_
}

感谢@Arvid,他向我介绍了std::aligned_storage,我相信该标准第20.10.7.6节中有一个明确的(尽管不是规范性的(答案(我认为它与N4141相同(。

首先,表57提到aligned_storage"成员typedef type应为POD类型。。。",其中9.0.10明确表示"POD结构是一个非并集类,既是平凡类又是标准布局类。">

接下来,20.10.7.6.1给出了一个非规范性的示例实现:

template <std::size_t Len, std::size_t Alignment>
struct aligned_storage {
  typedef struct {
    alignas(Alignment) unsigned char __data[Len];
  } type;
};

很明显,alignas的使用并不能阻止一个类型成为标准布局。