Ленивая инициализация элементов std :: tuple - PullRequest
0 голосов
/ 27 ноября 2018

Я часто использую 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;
};

1 Ответ

0 голосов
/ 27 ноября 2018

Доступ ко всему, что возвращается as_tuple(), является неопределенным поведением , поскольку это нарушает правила наложения типов.Пожалуйста, обратитесь к https://en.cppreference.com/w/cpp/language/reinterpret_cast:

Всякий раз, когда делается попытка прочитать или изменить сохраненное значение объекта типа DynamicType через glvalue типа AliasedType, поведение не определено, если только один изверно следующее:

  • AliasedType и DynamicType похожи.

  • AliasedType - это (возможно, cv-квалифицированный) вариант со знаком или без знака DynamicType.

  • AliasedType - это std :: byte, (начиная с C ++ 17) char или unsigned char: это позволяет проверить представление объекта любого объекта в виде массива байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...