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.