Использует std:: optional <int> так же эффективно, как и использование int?
У меня есть структура данных quad/octree. Im хранит дочерние индексы /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
. Таким образом, я могу определить, отсутствует ли в ячейке дочерний элемент, используя значение '' magic '', например -1
для Integer = int
, или std::numeric_limits<unsigned>::max()
для Integer = unsigned
. Это то, что std::optional<Integer>
не может принять.
Насколько я понял, это использование магических значений является одним из значений raison d'être std::optional
. Тем не менее, я беспокоюсь о производительности std::vector<std::optional<int>>
во внутренних циклах.
Итак,
-
Будет ли производительность std::vector<std::optional<int>>
хуже, чем у std::vector<int>
? (Я уже делаю сравнение для "несуществующего" значения).
-
Или можно оптимизировать реализацию std::optional
, чтобы предлагать такую же производительность, как raw int
? И как?
Смешение std::optional
в возвращаемом типе моих функций и магических значений в моей структуре данных звучит как очень плохая идея. Я предпочитаю быть последовательным и использовать один или другой (по крайней мере, в том же контексте). Хотя я могу перегрузить функцию, которая выполняет сравнение с магическим числом:
template<T> bool is_valid(const T& t) {
return /* comparison with magic value for t */;
}
для дополнительных типов.
Ответы
Ответ 1
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>
.
Применять это в шаблон с помощью только той, которая должна быть определена для каждого типа, должна быть довольно простой.
Ответ 2
Нет, это не так эффективно. Как вы можете видеть из эталонной реализации, он должен хранить, обновлять и проверять дополнительное значение.