Использование std:: optional так же эффективно, как использование 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 тип может представлять. Таким образом, я могу определить, отсутствует ли ячейка ребенка, используя "магическое" значение, такое как -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 оптимизируйте для предложения такая же производительность, как raw int? И как?

для смешивания std::optional в возвращаемом типе моих функций и магических значений в моей структуре данных звучит как очень плохая идея. Я предпочитаю быть последовательным и использовать один или другой (по крайней мере, в том же контексте). Хотя я мог бы перегрузить функцию, которая выполняет сравнение с магическим числом:

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

для необязательное типов.

2 ответов


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>.

сделать это в шаблон, только с sentinel необходимо определить для каждого типа, должно быть довольно простой.


нет, это не так эффективно. Как вы можете видеть из эталонной реализации он должен хранить, обновлять и проверять дополнительную ценность.