суббота, 13 сентября 2025 г.

[prog.c++] Наткнулся на любопытное с std::ranges::views::iota

Вчера мое утро началось с того, что под Linux-ом компилятор GCC 13 отказался компилировать код вроде вот такого (это не реальный код, а минимизированая версия на которой проблема воспроизводится):

templatetypename T, typename Range >
[[nodiscard]]
T
make_from( const Range & r )
{
   return T{ r.begin(), r.end() };
}

int main()
{
   std::string src{ "Hello, World" };
   auto v = make_from< values_container >(
         std::ranges::views::iota( 0u, src.size()) );

   std::cout << v.front() << " - " << v.back() << std::endl;
}

Изначально код был написан под другую платформу с более свежим С++ным компилятором, поэтому там make_from был реализован иначе, проблем с компиляцией не было. А вот под уже довольно древним по современным меркам GCC возникли проблемы: компилятор не видел у values_container конструктора, который бы получал два итератора.

Конкретно в моем случае в качестве values_container был folly::fbvector, но это не суть. Вполне мог бы быть и std::vector, проблема была бы точно такой же.

Ошибка несколько обескуражила, т.к. я точно знал, что у values_container есть конструктор, принимающий итераторы first и last.

Но с этим-то удалось разобраться достаточно быстро: first и last должны быть одного типа. А в моем случае объект-рэндж, возвращенный вызовом iota, имел разные типы для begin() и для end(). Соответственно, раз типы итераторов разные, то и компилятор не может найти подходящий конструктор у values_container.

Ну OK, раз типы у итераторов разные, то можно к этой ситуации приспособить реализацию make_from. Например, написать ее так: