За что я "люблю" эту концепцию 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) и т.д.
Вопрос:
Где как можно выйти в такой ситуации. Вроде задача элементарная, но времени отняла уже прилично. Так и чувствую себя как ослик бегущий за морковкой, ногу вытащишь, хвост увязнет и т.д.