std::tuple 个元素的延迟初始化
Lazy initialization of std::tuple elements
我经常使用std::aligned_storage
来指定一个未初始化的class成员。典型的例子是一个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;
};
访问 as_tuple()
返回的任何内容是 未定义的行为,因为它违反了类型别名规则。请参考https://en.cppreference.com/w/cpp/language/reinterpret_cast:
Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:
AliasedType and DynamicType are similar.
AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.
我经常使用std::aligned_storage
来指定一个未初始化的class成员。典型的例子是一个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;
};
访问 as_tuple()
返回的任何内容是 未定义的行为,因为它违反了类型别名规则。请参考https://en.cppreference.com/w/cpp/language/reinterpret_cast:
Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:
AliasedType and DynamicType are similar.
AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.