3

テンプレートを使用して、自明でない型の共用体の構築を簡素化したいと考えています。以下は実際には「機能する」ように見えますが、仕様では技術的には合法ではありません。

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 によると) 両方の構造が標準レイアウト タイプである場合にのみ、共用体 (つまり、フィールド) 内の 2 つの構造の共通の初期シーケンスにアクセスできることです。destructor_少なくとも 9.5 の非規範的な注意に従ってください。 .1. 9.0.7 によると、標準レイアウト型は非標準レイアウトの非静的データ メンバーを持つことはできません。したがって、A または B のいずれかが標準レイアウトでない場合destructor_、間違った共用体でアクセスすることは違法になります。

抜け穴は、に変換してunion_entry標準レイアウトを作成value_することのようですalignas(T) char[sizeof(T)]。9.0.7 では、alignas. したがって、私の質問: 次はどのタイプの標準レイアウト タイプTですか? したがって、前の例をエミュレートするためvalue_にキャストできますが、アクティブでない ?T&destructor_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<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_
}
4

1 に答える 1

0

を指摘してくれた@Arvidのおかげstd::aligned_storageで、標準のセクション20.10.7.6に決定的な(非規範的ではありますが)答えがあると思います(これはN4141と同じだと思います)。

まず、表 57 は、aligned_storage「メンバー typedeftype は 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しても、型が標準レイアウトになることを妨げないことは明らかです。

于 2016-10-13T05:49:10.123 に答える