Почему не удается скомпилировать std::представления::take(std::uint64_t{})?

#c #stl #standards #c 20 #std-ranges

Вопрос:

 #include <cstdint>
#include <ranges>

int main()
{
    auto const il = {1, 2, 3, 4, 5, 6};
    auto const n1 = std::int32_t{3};
    auto const n2 = std::uint32_t{3};
    auto const n3 = std::int64_t{3};
    auto const n4 = std::uint64_t{3};

    il | std::views::take(n1); // ok
    il | std::views::take(n2); // ok
    il | std::views::take(n3); // ok
    il | std::views::take(n4); // error
}
 

Смотрите онлайн-демонстрацию

Почему не удается скомпилировать std::views::take(std::uint64_t{}) ?

Комментарии:

1. к вашему сведению — работа в 11.2 — live — godbolt.org/z/hnKPn4soz

2. Похоже, это ошибка gcc 11.1.

3. wg21.ссылка/p2367r0

Ответ №1:

До P2367 регистр по умолчанию views::take(E, F) был указан как эквивалентный выражению ranges::take_view{E, F} . Обратите внимание на фигурные скобки.

ranges::take_view<V> принимает a range_difference_t<V> (который подписан) в качестве второго аргумента. Инициализация списка отклоняет сужение (например, попытка инициализации a ptrdiff_t с помощью a uint64_t ), поэтому ваш пример был отклонен.

Однако теперь views::take(E, F) указано, что выражение эквивалентно ranges::take_view(E, F) . Со скобками. Что означает отсутствие проверки на сужение, что означает, что прохождение a uint64_t абсолютно нормально.

libstdc реализовал это изменение 30 апреля, которое, я думаю, было слишком поздно для gcc 11.1, но это изменение есть в gcc 11.2, который принимает ваш код.

Ответ №2:

range | std::views::take(n) является выражением, эквивалентным std::ranges::take_view(range, n) , которое принимает a ranges::range_difference_t<V> .

Следуя псевдонимам, gcc 11.1 не хочет unsigned long считать пригодным для использования там, где long требуется a