Я часто использую std::aligned_storage
для указания неинициализированного члена класса.Типичным примером является static_vector, который хранит свои элементы в структуре.
Однако я не совсем уверен, что мне следует делать, когда я хочу, чтобы std::tuple
было создано шаг за шагом , инициализация его членов в неопределенном порядке в разные моменты времени.
Было бы законно создать
std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>
, а затем переосмыслить ссылку на элемент как std::tuple<Types...>&
?
Например:
#include <bitset>
#include <memory>
#include <new>
#include <tuple>
#include <utility>
template < class... Ts >
class uninitialized_tuple {
public:
using tuple_type = typename std::tuple<Ts...>;
using buffer_type =
std::tuple<
typename std::aligned_storage<sizeof(Ts),alignof(Ts)>::type...
>;
~uninitialized_tuple() {
destruct_helper<std::index_sequence_for<Ts...>>::erase(*this);
}
tuple_type& as_tuple() {
reinterpret_cast<tuple_type&>(_storage);
}
bool valid() const {
return _is_set.all();
}
template < size_t index, class... Args >
void emplace( Args&&... args ) {
using element_type = typename std::tuple_element<index,tuple_type>::type;
new (&std::get<index>(_storage)) element_type( std::forward<Args>(args)...);
_is_set.set(index);
}
template < size_t index >
void erase() {
using element_type = typename std::tuple_element<index,tuple_type>::type;
if( _is_set[index] ) {
std::get<index>(_storage).~element_type();
_is_set.reset(index);
}
}
private:
template < class Seq >
struct destruct_helper {
static void erase( uninitialized_tuple& ) {}
};
template < size_t index, size_t... indices >
struct destruct_helper<std::index_sequence<index,indices...>> {
static void erase( uninitialized_tuple& value ) {
value.erase<index>();
destruct_helper<std::index_sequence<indices...>>::erase_one(value);
}
};
buffer_type _storage;
std::bitset<sizeof...(Ts)> _is_set;
};