我有四/八树数据结构。我将一个单元格的子索引/ 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 */;
}
表示可选类型。
答案 0 :(得分:14)
std::optional
将需要额外的存储空间并将更少的值放入缓存中(看来你已经知道了这个原因)。
我不认为在数据结构内部存储与公共API公开的值不同的值是不正确的,只要内部表示对用户完全隐藏即可。
此外,我建议您将幻数分隔为一对inline
转换函数。
编译器应该帮助您记住一致地使用转换函数,如果忘记则生成类型错误。您甚至可以在内部数据结构中为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>
。
将其变成模板,只需要为每种类型定义标记,应该非常简单。
答案 1 :(得分:3)
不,它效率不高。正如您从reference implementation所看到的,它必须存储,更新并检查额外的值。