Могут ли C ++ 14 стандартные типы макетов использовать `alignas` для полей?

Я хотел бы использовать шаблоны, чтобы упростить конструирование союзов с нетривиальными типами. Следующее, кажется, «работает» на практике, но технически не является законным по спецификации:

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_ в неправильном союзе.

Казалось бы, лазейка 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_
}

3

Решение

Спасибо @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 не мешает типу быть стандартным макетом.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]