Пользовательские вариативная мин/макс/сумма функций


Я решил написать свою собственную min max sum функции. Мне очень не нравятся те, что предоставленная стл, поэтому я решил написать свою собственную.

#include <type_traits>
namespace utils {
namespace detail {
    template<typename T1, typename T2>
    using BiggerT = typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>;
    template<typename T1, typename T2>
    using FloatingPointOverBiggerT = typename std::conditional < std::is_floating_point<T1>::value || std::is_floating_point<T2>::value,
        double, typename BiggerT<T1, T2>>;

    template<typename...> struct BiggestType;

    template<typename T1, typename T2, typename... RestT>
    struct BiggestType<T1, T2, RestT...> {
        using type = typename FloatingPointOverBiggerT<T1, typename BiggestType<T2, RestT...>::type>::type;
    };

    template<typename T>
    struct BiggestType<T> { 
        using type = T; 
    };

    template<typename T1, typename T2>
    struct BiggestType<T1, T2>{ 
        using type = typename FloatingPointOverBiggerT<T1, T2>::type;
    };

    template<typename FirstNumericT, typename SecondNumericT>
    constexpr decltype(auto) Max(FirstNumericT&& first, SecondNumericT&& second) noexcept {
        using Type = typename detail::BiggestType<FirstNumericT, SecondNumericT>::type;
        return (static_cast<Type>(first) > static_cast<Type>(second)) ? static_cast<Type>(first) : static_cast<Type>(second);
    }

    template<typename FirstNumericT, typename SecondNumericT, typename... RestNumericT>
    constexpr decltype(auto) Max(FirstNumericT&& first, SecondNumericT&& second, RestNumericT&&... restargs) noexcept {
        using Type = typename detail::BiggestType<FirstNumericT, SecondNumericT, RestNumericT...>::type;
        return Max((static_cast<Type>(first) > static_cast<Type>(second)) ?
            static_cast<Type>(first) : static_cast<Type>(second), std::forward<RestNumericT&&>(restargs)...);
    }

    template<typename FirstNumericT, typename SecondNumericT>
    constexpr decltype(auto) Min(FirstNumericT&& first, SecondNumericT&& second) noexcept {
        using Type = typename detail::BiggestType<FirstNumericT, SecondNumericT>::type;
        return (static_cast<Type>(first) < static_cast<Type>(second)) ? static_cast<Type>(first) : static_cast<Type>(second);
    }

    template<typename FirstNumericT, typename SecondNumericT, typename... RestNumericT>
    constexpr decltype(auto) Min(FirstNumericT&& first, SecondNumericT&& second, RestNumericT&&... restargs) noexcept {
        using Type = typename detail::BiggestType<FirstNumericT, SecondNumericT, RestNumericT...>::type;
        return Min((static_cast<Type>(first) < static_cast<Type>(second)) ?
            static_cast<Type>(first) : static_cast<Type>(second), std::forward<RestNumericT&&>(restargs)...);
    }

    template<typename LastNumericT>
    constexpr decltype(auto) Sum(LastNumericT last) {
        return last;
    }

    template<typename FirstNumericT, typename SecondNumericT, typename... RestNumericT>
    constexpr decltype(auto) Sum(FirstNumericT&& first, SecondNumericT&& second, RestNumericT&&... restargs) noexcept {
        using Type = typename detail::BiggestType<FirstNumericT, SecondNumericT, RestNumericT...>::type;
        return Sum(static_cast<Type>(first) + static_cast<Type>(second), std::forward<RestNumericT&&>(restargs)...);
    }

    template < typename... >
    using void_t = void;
    template < typename T, typename Index >
    using SubscriptT = decltype(std::declval<T>()[std::declval<Index>()]);
    template < typename, typename Index = size_t, typename = void_t<> >
    struct HasSubscriptOperator : std::false_type {};
    template < typename T, typename Index >
    struct HasSubscriptOperator< T, Index, void_t< SubscriptT<T, Index> > > : std::true_type {};

}

template<typename ReturnT = int, typename ContainerT, typename PredicateT, typename RawT =
typename std::remove_cv<typename std::remove_reference<ReturnT>::type>::type>
constexpr typename std::enable_if < utils::detail::HasSubscriptOperator<ContainerT>::value, RawT>::type
Max(const ContainerT& container, const PredicateT& predicate) {
    auto result = predicate(*std::cbegin(container));
    for(const auto& num : container) {
        if(predicate(num) > result) {
            result = predicate(num);
        }
    }
    return result;
}

template<typename FirstT, typename SecondT, typename... NumericTypes>
constexpr typename std::enable_if < std::is_floating_point<FirstT>::value || std::is_integral<FirstT>::value,
typename utils::detail::BiggestType<FirstT, SecondT, NumericTypes...>::type>::type
Max(FirstT&& first, SecondT&& second, NumericTypes&&... args) noexcept {
    return detail::Max(std::forward<FirstT&&>(first), std::forward<SecondT&&>(second), std::forward<NumericTypes&&>(args)...);
}

template<typename ContainerT>
constexpr decltype(auto) Max(const ContainerT& container) {
    typename std::remove_cv<typename std::remove_reference<decltype(container[0])>::type>::type result = container[0];
    for(const auto& num : container) {
        if(num > result) {
            result = num;
        }
    }
    return result;
}

template<typename FirstT, typename SecondT, typename... NumericTypes>
constexpr typename std::enable_if < std::is_floating_point<FirstT>::value || std::is_integral<FirstT>::value,
typename utils::detail::BiggestType<FirstT, SecondT, NumericTypes...>::type>::type  
Min(FirstT&& first, SecondT&& second, NumericTypes&&... args) noexcept {
    return detail::Min(std::forward<FirstT&&>(first), std::forward<SecondT&&>(second), std::forward<NumericTypes&&>(args)...);
}

template<typename ReturnT = int, typename ContainerT, typename PredicateT, typename RawT =
typename std::remove_cv<typename std::remove_reference<ReturnT>::type>::type>
constexpr typename std::enable_if < utils::detail::HasSubscriptOperator<ContainerT>::value, RawT>::type
Min(const ContainerT& container, const PredicateT& predicate) {
    auto result = predicate(*std::cbegin(container));
    for(const auto& num : container) {
        if(predicate(num) < result) {
            result = predicate(num);
        }
    }
    return result;
}

template<typename ContainerT>
constexpr decltype(auto) Min(const ContainerT& container) {
    typename std::remove_cv<typename std::remove_reference<decltype(container[0])>::type>::type result = container[0];
    for(const auto& num : container) {
        if(num < result) {
            result = num;
        }
    }
    return result;
}

template<typename ReturnT = int, typename ContainerT, typename PredicateT, typename RawT =
typename std::remove_cv<typename std::remove_reference<ReturnT>::type>::type>
constexpr typename std::enable_if < utils::detail::HasSubscriptOperator<ContainerT>::value, RawT>::type
Sum(const ContainerT& container, PredicateT predicate) {
    RawT result = 0;
    for(const auto& num : container) {
        result += predicate(num);
    }
    return result;
}

template<typename FirstT, typename SecondT, typename... NumericTypes>
constexpr typename std::enable_if < std::is_floating_point<FirstT>::value || std::is_integral<FirstT>::value, 
typename utils::detail::BiggestType<FirstT, SecondT, NumericTypes...>::type>::type 
Sum(FirstT&& first, SecondT&& second, NumericTypes&&... args) noexcept {
    return detail::Sum(std::forward<FirstT&&>(first), std::forward<SecondT&&>(second), std::forward<NumericTypes&&>(args)...);
}

template<typename ContainerT>
constexpr decltype(auto) Sum(const ContainerT& container) {
    typename std::remove_cv<typename std::remove_reference<decltype(container[0])>::type>::type result = 0;
    for(const auto& num : container) {
        result += num;
    }
    return result;
}

template<typename FirstT, typename SecondT, typename... NumericTypes>
constexpr decltype(auto) Avg(FirstT&& first, SecondT&& second, NumericTypes&&... args) noexcept {
    return (detail::Sum(std::forward<FirstT&&>(first), std::forward<SecondT&&>(second), std::forward<NumericTypes&&>(args)...) / (2 + sizeof...(NumericTypes)));
}

Использование

std::vector<int> vecInt = {2, 3, 5, 2, 10};
    std::map<int, std::size_t> mapInt = {{2, 2}, {3, 3}, {7, 7}};
    int cInt[] = {2, 2, 2, 2};
    auto vecSum = utils::Sum(vecInt);
    auto mapSum = utils::Sum(mapInt, [](const auto& val) {return val.second;});
    auto cSum = utils::Sum(cInt);
    auto sum = utils::Sum(5, 7ULL, 5.5f, 70.0);
    auto vecMax = utils::Max(vecInt);
    auto mapMax = utils::Max(mapInt, [](const auto& val) {return val.second;});
    auto cMax = utils::Max(cInt);
    auto maxx = utils::Max(5, 55, 11, 33.0f, 55.0, 66ULL);
    auto vecMin = utils::Min(vecInt);
    auto mapMin = utils::Min(mapInt, [](const auto& val) {return val.second;});
    auto cMin = utils::Min(cInt);
    auto minn = utils::Min(5, 55, 11, 33.0f, 55.0, 66ULL);
    auto avgg = utils::Avg(5, 55, 11, 33.0f, 55.0, 66ULL);

Что вы думаете о коде?



Комментарии
2 ответа

Это рассчитано?

Это действительно трудно понять, что вы пытаетесь достичь здесь. Ваш код предотвращает некоторые основные использование max функции, например:

unsigned int u = 5u; 
int i = 7;
std::cout << "max(" << u << ", " << i << ") = " << utils::Max(u, i);

результаты в:


прог.чч:35:44: ошибка: неконстантную ссылку lvalue в тип 'беззнаковый инт' не могу связать значение неродственного типа 'int'
возвращение (static_cast(в первые) > static_cast(в секунду)) ? static_cast(в первую очередь) : static_cast(в секунду);
^ ~~~~~~

прог.чч:80:63: примечание: в инстанцирование шаблона функции специализацией утилиты::Макс просил здесь
с std::соиь << "Макс(" << у << "," << я << ") =" << утилиты::Макс(у меня);
^

прог.чч:35:100: ошибка: неконстантную ссылку lvalue в тип 'беззнаковый инт' не могу связать значение неродственного типа 'int'
возвращение (static_cast(в первые) > static_cast(в секунду)) ? static_cast(в первую очередь) : static_cast(в секунду);
^ ~~~~~~
2 ошибки.

Это означает, что вы не можете использовать 'числовой' перегрузки, но с литеральными значениями, будь то в качестве аргументов или возвращаемого типа. Это действительно строгими. Если я хочу уменьшить наибольшее значение из двух, я не могу, например.

На данный момент, я не уверен, является ли это предполагаемое поведение, или если вы должны исправить свой код перед отправкой.

Гипотетический комментарий

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


  • комментируя ваш код немного: хорошие комментарии в эти дни о намерениях, и это то, что ваши читатели будут пользоваться.

  • <limits> это, я думаю, что вы ищете при сравнении типов. sizeof не скажу вам вещь о диапазоне значений типа может представлять. Подумайте, что вы должны сделать при сравнении int и unsigned int, который, как правило, имеют одинаковый размер: выбранный тип не должен быть таким же, если вы называете min или max...

  • max и max_element разделяются в стандартной библиотеке. Зачем тебе их слить? Перегрузка всегда была непростым делом, и вместе с шаблоны для нечитаемые сообщения об ошибках, так что не делайте этого, если есть хороший повод.

  • есть containers что не дают operator[]. В std::list выходит за рамки вашего Max функция по непонятным причинам. И семантику std::map может удивить, поскольку любой человек с базовыми знаниями из стл считает const std::pair<Key, T> для элементов типа.

4
ответ дан 16 апреля 2018 в 07:04 Источник Поделиться

Существует невероятное количество шаблонный. Я хотел бы напомнить вам, что в C++17 вы можете использовать фолд выражения, которые делают то же, но проще:

template<typename ... T>
auto sum(T ... t) {
return (t + ...);
}

Если вы хотите быть уверены, что пустая сумма также будет компилироваться вы можете сделать это:

template<typename ... T>
auto sum(T ... t) {
return (t + ... + 0);
}

Вы, очевидно, может расшириться, что в разных видах и т. д

1
ответ дан 16 апреля 2018 в 06:04 Источник Поделиться