简体   繁体   English

C ++ 14标准布局类型可以使用`alignas`作为字段吗?

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

I'd like to use templates to simplify the construction of unions with non-trivial types. 我想使用模板来简化具有非平凡类型的联合的构造。 The following seems to "work" in practice, but is technically not legal by the spec: 以下似乎在实践中“起作用”,但在规范上技术上并不合法:

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...
};

The problem is that (according to N4141) you can access a common initial sequence of two structures in a union (ie, the destructor_ field) only if both structures are standard-layout types--at least according to a non-normative note in 9.5.1. 问题在于(根据N4141),只有当两个结构都是标准布局类型时,才能访问联合中两个结构的公共初始序列(即destructor_字段) - 至少根据非规范性说明9.5.1。 According to 9.0.7, a standard-layout type can't have any non-static data members with non-standard layout. 根据9.0.7,标准布局类型不能具有任何具有非标准布局的非静态数据成员。 So if either A or B is not standard-layout, then it becomes illegal to access destructor_ in the wrong union. 因此,如果A或B不是标准布局,那么在错误的并集中访问destructor_就变得非法。

A loophole would seem to be to make union_entry standard layout by turning value_ in to an alignas(T) char[sizeof(T)] . 漏洞似乎是通过将value_ in转换为alignas(T) char[sizeof(T)]来制作union_entry标准布局。 Nothing in 9.0.7 appears to rule out the use of the alignas . 9.0.7中的任何内容似乎都不排除使用alignas Thus, my question: Is the following a standard-layout type for any type T ? 因此,我的问题对于任何类型T ,以下是标准布局类型吗? And hence can value_ to be cast to T& to emulate the previous example, while still allowing destructor_ to be used in a non-active union_entry ? 因此可以将value_转换为T&以模拟前面的示例,同时仍允许destructor_用于非活动的union_entry

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

In both clang-3.8.1 and g++-6.2.1, std::is_standard_layout suggests union_entry<T> is standard layout even when T is not. 在clang-3.8.1和g ++ - 6.2.1中, std::is_standard_layout表明union_entry<T>是标准布局,即使T不是。 Here's a complete working example of how I would like to use this technique: 这是一个完整的工作示例,说明我将如何使用此技术:

#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_
}

Thanks to @Arvid, who pointed me to std::aligned_storage , I believe there is a definitive (albeit non-normative) answer in section 20.10.7.6 of the standard (which I assume is the same as N4141). 感谢@Arvid,他指向我std::aligned_storage ,我相信标准的第20.10.7.6节中有一个明确的(尽管是非规范的)答案(我假设与N4141相同)。

First, Table 57 says of aligned_storage "The member typedef type shall be a POD type ...", where 9.0.10 makes clear "A POD struct is a non-union class that is both a trivial class and a standard-layout class ." 首先,表57说明aligned_storage “成员type 应为POD类型 ...”,其中9.0.10表明“POD结构是一个非联合类,既是一个普通类, 也是一个标准布局类 “。

Next, 20.10.7.6.1 gives a non-normative example implementation: 接下来,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;
};

So clearly the use of alignas does not prevent a type from being standard-layout. 很明显,使用alignas并不能防止类型成为标准布局。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM