问题 使用std :: optional 与使用int一样有效吗?


我有一个四/八树数据结构。我将一个单元格的子索引/ ptrs存储在一个数组中。数组中的每个位置代表一个孩子相对于其父母的位置,例如在2D中:

// _____________
// |     |     |
// |  2  |  3  |
// |_____|_____|
// |     |     |
// |  0  |  1  |
// |_____|_____|
// for each cell, 4 children are always stored in row-major order
std::vector<std::array<Integer,4>> children;

我知道孩子的最大数量是一个值的子集 Integer 类型可以代表。因此,我可以通过使用像'魔术'这样的值来识别一个细胞是否遗漏了一个孩子 -1 对于 Integer = int, 要么 std::numeric_limits<unsigned>::max() 对于 Integer = unsigned。这是那样的 std::optional<Integer> 不能假设。

据我所知,这种魔法价值的使用是其中一个存在的理由 std::optional。不过,我担心的是表现 std::vector<std::optional<int>> 在内循环中。

所以,

  • 请问表现 std::vector<std::optional<int>> 比那更糟糕 std::vector<int>? (我已经在对“不存在的”值进行比较了)。

  • 或者,可以执行 std::optional 经过优化,可提供与原始相同的性能 int?如何?

混合 std::optional 在我的函数的返回类型和我的数据结构中的魔术值听起来是一个非常糟糕的主意。我更喜欢保持一致,要么使用其中一个(至少在同一个上下文中)。虽然我可以重载与幻数进行比较的函数:

template<T> bool is_valid(const T& t) { 
  return /* comparison with magic value for t */; 
}

对于可选类型。


10769
2018-06-26 15:27


起源

不确定两者之间的表现,这是你必须亲自看到的东西。但问题是,为您使用比特信息可行的解决方案?由于您正在存储索引,您是否可以在单个int中编码有关4个子indeces的信息?由于你有4个孩子,你可以为每个孩子使用4个字节(或更多),总共16个字节用于indeces,另外16个你可以用来编码它是否有效?创建一个类来处理这个问题,这样用户就不必担心内部问题了 - dchhetri
存在的理由 std::optional,恰恰是你的情况 别 有一个哨兵价值。如果你有一个“明显的”哨兵,你为什么不使用它? - André Caron
@ user814628每个索引都是一个unsigned long long ...我有很多单元格,不能做你的建议。 - gnzlbg
@AndréCaron:它会使代码更清晰(更清晰地表达意图)。实际上,如果你可以为用户定义的类型声明一个sentinel值并告诉它,那就太好了 optional 使用它。你甚至可以将该哨兵值的构造私有化 std::optional<yourtype> 作为朋友。 - celtschk
如果你使用 vector<compact_optional<int>> 您可以获得类型安全性和零性能开销。尝试: github.com/akrzemi1/compact_optional - Andrzej


答案:


std::optional 将需要额外的存储空间并将更少的值放入缓存中(看起来您已经知道了这个原因)。

我不认为在数据结构内部存储与公共API公开的值不同的值是不正确的,只要内部表示对用户完全隐藏即可。

此外,我建议你将魔术数字分成一对 inline 转换功能。

编译器应该帮助您记住一致地使用转换函数,如果忘记则生成类型错误。您甚至可以使用精简的struct包装器 int 在内部数据结构中,确保不存在隐式转换(或定义用户定义的转换)。

class CompressedOptionalUInt
{
    static const unsigned SENTINEL_MISSING = std::numeric_limits<unsigned>::max();
    unsigned value;

public:
    CompressedOptionalUInt(std::optional<unsigned> val) : value(!val? SENTINEL_MISSING: *val) {}
    operator std::optional<unsigned>() const { ... }
};

然后使用 std::array<CompressedOptionalUInt>

将其转换为模板,只需要为每种类型定义标记,应该非常简单。


12
2018-06-26 15:39



谢谢!使用薄的结构包装听起来像个好主意! - gnzlbg


不,它效率不高。从你可以看到 参考实施 它必须存储,更新和检查额外的值。


3
2018-06-26 15:43