【问题标题】:Lazy initialization of std::tuple elementsstd::tuple 元素的延迟初始化
【发布时间】:2018-11-26 21:15:14
【问题描述】:

我经常使用std::aligned_storage 来指定一个未初始化的类成员。典型的例子是一个 static_vector,它将其元素存储在结构中。

但是,当我想逐步创建std::tuple,并在不同时间点以未指定的顺序初始化其成员时,我不完全确定我应该怎么做。

创建是否合法

std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>

然后将成员引用重新解释为std::tuple&lt;Types...&gt;&amp;?

例如:

#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;
};

【问题讨论】:

  • 我认为这不合法。 AFAIK 类型 std::aligned_storage&lt;sizeof(Types),alignof(Types)&gt;::type... 的大小可以大于 Types...,这会给你不对齐的访问权限。
  • as_tuple 对我来说似乎是未定义的行为。您可能希望将此标记为language-lawyer
  • 当然无效。您不能将 std::tuple&lt;X&gt;std::tuple&lt;Y&gt; 别名。
  • @NathanOliver 它们只是完全不相关的类型,所以首先,这是一个严格的别名违规。但即使不考虑这一点,标准中也没有任何内容表明std::tuple&lt;X&gt; 可能与std::tuple&lt;Z&gt; 有任何兼容的布局。
  • 您无法访问从未创建过的对象。创建元素是不够的。考虑到您的代码,您不需要太多帮助。您最好将每个元素包装在 union unitialized{ element_type value; char c='\0';uninitialized()=default;....~unitialized(){}} 中,并使您 uninitialized_tuple a... tuple => 实现 get,并专门化 tuple_size 和 tuple_element。

标签: c++ c++14 lazy-initialization stdtuple


【解决方案1】:

访问as_tuple() 返回的内容是未定义的行为,因为它违反了类型别名规则。请参考https://en.cppreference.com/w/cpp/language/reinterpret_cast

每当尝试通过 AliasedType 类型的 glvalue 读取或修改 DynamicType 类型的对象的存储值时,除非满足以下条件之一,否则该行为是未定义的:

  • AliasedType 和 DynamicType 类似。

  • AliasedType 是 DynamicType 的(可能是 cv 限定的)有符号或无符号变体。

  • AliasedType 是 std::byte、(C++17 起)char 或 unsigned char:这允许将任何对象的对象表示检查为字节数组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    相关资源
    最近更新 更多