Элегантность кода благодаря C++23
Продолжим разбирать приёмы рефакторинга и посмотрим, как std::ranges::views::enumerate помогает сделать код более элегантным.
В статье "C++: Пиши, сокращай, оптимизируй" я рассматривал рефакторинг и оптимизацию кода за счёт объединения циклов. В итоге я остановился на следующем варианте кода:
static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes) { auto q = sizes.size(); std::vector<int64_t> strides(q); int64_t acc = 1; int64_t ne = 1; for (auto sz : std::ranges::views::reverse(sizes)) { strides[--q] = acc; acc *= (sz == 0 ? 1 : sz); ne *= sz; } //.... }
Его недостаток в том, что всё равно необходимо использовать переменную q для работы с контейнером strides. Как можно написать ещё лаконичнее, я не сообразил, но такой способ есть!
После публикации мне подсказали про enumerate. Эта штука появилась в C++23, и я как-то её пропустил. Сложно уследить за всеми нововведениями C++.
С помощью enumerate можно сразу перебирать и элементы, и их индексы:
constexpr static auto v = {'A', 'B', 'C', 'D'}; for (auto const [index, letter] : std::views::enumerate(v)) std::cout << '(' << index << ':' << letter << ") ";
Будет напечатано: (0:A) (1:B) (2:C) (3:D).
Но нам нужен обратный порядок, и такой вариант не подходит:
for (auto const [i, sz] : std::views::enumerate( std::ranges::views::reverse(sizes)))
Этот цикл будет перебирать элементы с конца, а индексы — по возрастанию от 0. Можно сделать, чтобы значения i также шли в обратном порядке? Можно. Это делается с помощью std::views::enumerate(sizes) | std::views::reverse.
В итоге можно сократить код ещё на одну строчку:
static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes) { std::vector<int64_t> strides(sizes.size()); int64_t acc = 1; int64_t ne = 1; for (auto const [i, sz] : std::views::enumerate(sizes) | std::views::reverse) { strides[i] = acc; acc *= (sz == 0 ? 1 : sz); ne *= sz; } //.... }
Нельзя назвать это большим достижением, но зато на практике применили одну из новых возможностей C++23.
Что со скоростью кода? Замер показал, что в рамках погрешности его скорость не изменилось. Т.е. этот вариант работает так же, как вариант №2 — "Мой вариант одним циклом" (см. замер скорости работы в статье). Это не удивительно, так как по сути код идентичен предыдущему.
Спасибо за внимание и подписывайтесь на наш дайджест, чтобы не пропустить интересное. Например, у нас грядёт выпуск PVS-Studio 8.0 с поддержкой новых языков.












