【问题标题】:Create dummy object of non-default-constructible class创建非默认可构造类的虚拟对象
【发布时间】:2015-07-05 04:52:06
【问题描述】:

tl;dr:我想构造一个包含泛型类型 Value 成员的类 ListEntry,但 Value 不是默认可构造的ListEntry 不知道如何构造它。我永远不会访问这个 Value 成员,所以它没有被初始化也没关系。

我为什么要这样做

我正在实现一个大致类似于以下内容的双链表

template<class Value>
class ListEntry {
  Value value;
  ListEntry<Value> *prev;
  ListEntry<Value> *next;
};
template<class Value>
class List {
  ListEntry<Value> sentinel;
};

列表条目之间的链接始终形成一个封闭的圆圈,其中哨兵​​将最后一个列表元素连接到第一个列表元素。 sentinel 对象使用 sentinel.prev = &sentinel 和 sentinel.next = &sentinel 初始化。

这样,我摆脱了很多特殊情况,而且我永远不必检查 nullptr,因为没有空指针。将一个元素添加到列表的末尾(在最后一个元素和标记之间)并不是一种特殊情况,但与在两个真实元素之间的列表中间添加一个元素相同。

所以在所有真实的列表条目中,值字段将包含列表条目的实际值。对于他们,我可以通过在其构造函数中给它一个 Value 对象来初始化 ListEntry,所以我不需要 Value 是默认可构造的。在哨兵中,值字段永远不会被访问。但不幸的是,由于 Value 不是默认可构造的,编译器不允许我创建哨兵对象。

我可以将 ListEntry 中的值成员设为指针、boost::optional 或类似的东西。由于性能问题,我不喜欢这个。 关于如何在 ListEntry 中存储 Value 而不需要性能/内存成本并且不需要 Value 默认构造的任何想法?在我看来,必须有一种方法可以在不调用其构造函数的情况下获取 Value 对象。

【问题讨论】:

    标签: c++ c++14 default-constructor sentinel


    【解决方案1】:

    使用原始缓冲区并放置新的:

    template<class Value>
    class ListEntry {
      alignas(Value) char storage[sizeof(Value)];
      ListEntry<Value> *prev;
      ListEntry<Value> *next;
    };
    

    构造Value

    new (entry->storage) Value(/* params */);
    

    破坏Value

    reinterpret_cast<Value*>(entry->storage)->~Value();
    

    【讨论】:

    • 这就是我要找的,谢谢。看着它,在我看来,这个概念(一个可能保存或可能不保存初始化对象的通用缓冲区,提供 new() 和 delete())足够通用,因此可以在 std 或 boost 中实现。有吗?
    • @Heinzi 不是我所知道的。缓冲区本身存在std::aligned_storage,但老实说我无法直截了当,所以我坚持使用纯字符数组。但实际上并没有更多的东西,所以最接近的应该是boost::optional,它还跟踪对象是否被构造。
    • boost::optional (它有一个布尔跟踪“它是否被构造”,你可能认为这是开销,但它不是,当你的对象被销毁时会发生什么?如果销毁是抛出一个意想不到的地方?)是一个不错的选择,或者来自 C++1z 的std::experimental::optional(端口,有一些变化)。另一个选择是struct nothing {}; boost::variant&lt;Value, nothing&gt;,它基本上是一个手动的optional,没有简单的cast-to-value和cast-to-bool。要自己滚动,请使用 C++11 union(如果需要销毁它,可能带有布尔值)和 nothingaligned_storage
    【解决方案2】:

    您可以将其拆分为基类和节点类,例如

    class ListEntryBase {
        ListEntryBase *prev;
        ListEntryBase *next;
    };
    
    template<class Value>
    class ListEntry : public ListEntryBase {
        Value value;
    };
    
    template<class Value>
    class List {
        ListEntryBase sentinel;
    };
    

    这样可以避免创建不需要的值,同时Value 不需要是默认可构造的。

    【讨论】:

    • 记得在向下转换之前检查你是否在哨兵上。
    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多