创建非默认可构造类

时间:2015-04-26 21:32:08

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

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 =&amp; sentinel和sentinel.next =&amp; sentinel初始化sentinel对象。

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

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

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

2 个答案:

答案 0 :(得分:6)

使用原始缓冲区和新位置:

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();

答案 1 :(得分:5)

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

class ListEntryBase {
    ListEntryBase *prev;
    ListEntryBase *next;
};

template<class Value>
class ListEntry : public ListEntryBase {
    Value value;
};

template<class Value>
class List {
    ListEntryBase sentinel;
};

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