【问题标题】:Can C++14 standard-layout types use `alignas` for fields?C++14 标准布局类型可以对字段使用“alignas”吗?
【发布时间】:2016-10-12 05:31:33
【问题描述】:

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

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&amp; 以模拟前面的示例,同时仍然允许 @ 987654331@ 用于非活动的union_entry

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&lt;T&gt; 是标准布局,即使 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_
}

【问题讨论】:

  • 你可能想看看 std::aligned_storage,但你也可能想看看 boost::variant。也许您可以将析构函数指针放在联合之外,因为大概每个成员都需要它。

标签: c++ c++14 standard-layout


【解决方案1】:

感谢@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 的使用不会阻止类型成为标准布局。

【讨论】:

    猜你喜欢
    • 2017-02-15
    • 2018-04-21
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 2016-12-25
    • 2015-09-21
    • 1970-01-01
    相关资源
    最近更新 更多