ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 25.12.20 17:21
Оценка:
За что я "люблю" эту концепцию STL так это за то, что наступаешь на грабли в самых казалось бы простых вещах!
Захотел написать обобщенный код, было:

    constexpr uint_t CalcSomething(const char* beg, const char* end) // ...
    constexpr uint_t CalcSomething(const char16_t* beg, const char16_t* end) // ...
    constexpr uint_t CalcSomething(const char32_t* beg, const char32_t* end) // ...
    constexpr uint_t CalcSomething(const wchar_t* beg, const wchar_t* end) // ...

Все отлично работает, был простой код. Не совсем гибко, а давайте еще обобщим и добавим что бы работало с любыми итераторами.
Проблема первая, как перегрузить итераторы только для перечисленных типов? Ну я ничего лучше не придумал чем (может кто подскажет как правильно):
    template<class Type>
    struct decay_with_const
    {    
        using NonRefType = std::remove_reference_t<Type>;

        using type = std::conditional_t<std::is_array_v<NonRefType>,
            std::add_pointer_t<std::remove_extent_t<NonRefType>>,
            std::conditional_t<std::is_function_v<NonRefType>,
            std::add_pointer_t<NonRefType>,
            std::remove_volatile_t<NonRefType>>>;
    };

    template<class _Ty>
    using decay_with_const_t = typename decay_with_const<_Ty>::type;

стандартный decay не подходит, так как убирает const.
    // Const iterator-like type check

    template<typename It, typename Value>
    struct const_iterator_of_type
    {
        static constexpr bool value = std::is_same_v<std::decay_t<decltype(*std::declval<It>())>, Value>;
    };

    template<typename It, typename Value>
    constexpr bool const_iterator_of_type_v = const_iterator_of_type<It, Value>::value;

Получилось так:
    template<typename It, std::enable_if_t<const_iterator_of_type_v<It, char>, int> = 0>
    constexpr uint_t CalcSomething(It beg, It end) // ...
    template<typename It, std::enable_if_t<const_iterator_of_type_v<It, char16_t>, int> = 0>
    constexpr uint_t CalcSomething(It beg, It end) // ...
    template<typename It, std::enable_if_t<const_iterator_of_type_v<It, char32_t>, int> = 0>
    constexpr uint_t CalcSomething(It beg, It end) // ...
    template<typename It, std::enable_if_t<const_iterator_of_type_v<It, wchar_t>, int> = 0>
    constexpr uint_t CalcSomething(It beg, It end) // ...

Вроде бы все работает, кроме последнего варианта с wchar_t, который на самом деле внутри ничего не делает, а вызывает нужную перегрузку:

    template<typename It, std::enable_if_t<const_iterator_of_type_v<It, wchar_t>, int>>
    constexpr uint_t CalcSomething(It beg, It end)
    {
        if constexpr (sizeof(wchar_t) == sizeof(char16_t))
            return CalcSomething<char16_t>(beg, end); // ????
        else if constexpr (sizeof(wchar_t) == sizeof(char32_t))
            return CalcSomething<char32_t>(beg, end); // ????
        else
            static_assert(!std::is_same_v<wchar_t, char16_t> && !std::is_same_v<wchar_t, char32_t>, "Unsuported wchar_t size!");

    }

И начаинается...
— преобразовать тип итераторов — я не могу
— перебразовать в указатели — я не могу, т.к. &*begin, &*end — нельзя разыменовывать если они указывают за конец данных (например у string_view) и т.д.

Вопрос:
Где как можно выйти в такой ситуации. Вроде задача элементарная, но времени отняла уже прилично. Так и чувствую себя как ослик бегущий за морковкой, ногу вытащишь, хвост увязнет и т.д.

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.