Как-то так?
static constexpr size_t idx(size_t i)
{
size_t j = 0;
if(!(... || (j++, i == Is))) {
throw "Index out of range!";
}
return j-1;
}
Может быть немного сложно читать, но делать то, что вы хотите, если я правильно понял. После создания экземпляра это в основном эквивалентно серии if else
, проходящей слева направо по индексам в Is
.
. Вы можете сделать его более читабельным, разделив тело выражения сгиба на лямбду. ,Вы также должны заменить выражение throw
на то, что подходит для вас.
С помощью квалификатора constexpr
это можно использовать и в шаблонной версии:
template<size_t I, auto s = idx(I)>
static constexpr size_t idx() {
return s;
}
(Установкарезультат в аргументе шаблона по умолчанию гарантирует оценку времени компиляции.)
Это не самый эффективный код, разделяющий те же проблемы, что и у написанного вручную оператора switch
. В зависимости от предсказуемости входных данных многие ветви часто могут быть неправильно предсказаны, и в этом случае (в основном) версия без ответвлений будет предпочтительнее. Это может быть достигнуто путем соответствующего изменения выражения сгиба.
Если число индексов не мало, цикл локального кэша инструкций предпочтительнее для локальности кэша команд:
static constexpr size_t idx(size_t i)
{
static constexpr std::array ind{Is...};
size_t j = 0;
for(; j < ind.size() && i != ind[j]; j++);
if(j == ind.size())
throw "Index out of range!";
return j;
}
Опять же, может быть предпочтительнее заменить ранний выход в цикле.
Если массив еще больше, может быть полезно не только использовать объявленный массив с циклом, но и реализовать двоичный поиск по этому массиву.
Стандартные алгоритмы, такие как std::any_of
, std::find
и std::binary_search
, могут использоваться вместо реализаций ручного поиска. Однако эти алгоритмы будут constexpr
только в C ++ 20, и это ограничит использование здесь.