Как найти constexpr max переменной number из constexpr std :: arrays со значением по умолчанию - PullRequest
0 голосов
/ 29 октября 2018

Итак, у меня есть число constexpr std::array<int, N> для различных значений N. В этом случае:

constexpr std::array<int, 3> r1 {1, 3, 5};
constexpr std::array<int, 2> r2 {3, 4};
constexpr std::array<int, 4> r3 {1, 2, 5, 6};
constexpr std::array<int, 2> r4 {2, 6};

Я хотел бы найти элемент constexpr max (а затем min) во всех array s. Кажется, это работает просто отлично:

constexpr int the_max() {
    return 0;
}

template<typename T, typename... Ts>
constexpr int the_max(T&& t, Ts&&... ts) {
    const int v = *std::max_element(t.cbegin(), t.cend());
    return std::max(v, the_max(ts...));
}

Как показано здесь:

constexpr auto max_entry = dlx::the_max(r1, r2, r3);
std::cout << max_entry << '\n';

, который печатает 6, как и ожидалось.

Однако, похоже, здесь должно быть больше логики, например:

  1. Значение по умолчанию (или минимальное); и

  2. Чтобы типы в std::array могли быть разными, если они все являются арифметическими типами.

Я чувствую, что это должно работать:

template<typename B>
constexpr std::enable_if_t<std::is_arithmetic_v<B>, B>
the_max2(B&& b) {
    return b;
}

template<typename B, typename T, typename... Ts>
constexpr std::enable_if_t<std::is_arithmetic_v<B> && std::is_arithmetic_v<T::value_type>, std::common_type_t<B, typename T::value_type>>
the_max2(B&& b, T&& t, Ts&&... ts) {
    const int v = *std::max_element(t.cbegin(), t.cend());
    return std::max(v, the_max2(ts...));
}

но это происходит с:

error: no matching function for call to 'the_max2<int>(int, const std::array<int, 3>&, const std::array<int, 2>&, const std::array<int, 4>&)'

и ожидает только 1 параметр, но получает 4, и:

 error: 'value_type' is not a member of 'const std::array<int, 3>&'

Кто-нибудь скажет мне, что я делаю не так? Любая помощь будет очень признательна.

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

Вам необходимо применить черту std::is_arithmetic к value_type передаваемых аргументов, а не к самим себе, вам также нужно удалить ссылки из параметров шаблона, так как вы используете переадресацию ссылок.

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

namespace impl {
    template <bool... Preds> struct all_dummy;
    template <bool... Preds> using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>;
}

template<typename T, typename... Ts>
constexpr std::enable_if_t<
    impl::all<
        std::is_integral<typename std::remove_reference_t<T>::value_type>::value
    >::value,
    typename std::remove_reference_t<T>::value_type
>
the_max2(T&& t) {
    const int v = *std::max_element(t.cbegin(), t.cend());
    return v;
}


template<typename T, typename... Ts, typename R = 
    std::common_type_t<
            typename std::remove_reference_t<T>::value_type,
            typename std::remove_reference_t<Ts>::value_type...>
>
constexpr std::enable_if_t<
    impl::all<
        std::is_integral<typename std::remove_reference_t<T>::value_type>::value,
        std::is_integral<typename std::remove_reference_t<Ts>::value_type>::value...
    >::value,
    R
>
the_max2(T&& t, Ts&&... ts) {
    const int v = *std::max_element(t.cbegin(), t.cend());
    return std::max<R>(v, the_max2(ts...));
}

если доступно , код можно упростить с помощью if constexpr

namespace impl {
    template <bool... Preds> struct all_dummy;
    template <bool... Preds> using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>;
}


template<typename T, typename... Ts, typename R =
    std::common_type_t<
        typename std::remove_reference_t<T>::value_type,
        typename std::remove_reference_t<Ts>::value_type...>
>
constexpr std::enable_if_t<
    impl::all<
        std::is_integral_v<typename std::remove_reference_t<T>::value_type>,
        std::is_integral_v<typename std::remove_reference_t<Ts>::value_type>...
    >::value,
    R
>
the_max2(T&& t, Ts&&... ts) {
    const int v = *std::max_element(t.cbegin(), t.cend());
    if constexpr (sizeof...(ts) > 0) {
        return std::max<R>(v, the_max2(ts...));
    } else {
        return v;
    }
}
0 голосов
/ 29 октября 2018

Некоторые проблемы, без определенного порядка

(1), как указано S.M., вы забыли typename до T::value_type

template<typename B, typename T, typename... Ts> // .......................VVVVVVVV
constexpr std::enable_if_t<std::is_arithmetic_v<B> && std::is_arithmetic_v<typename T::value_type>, std::common_type_t<B, typename T::value_type>>

(2) вы забыли b в рекурсивном вызове

// .........................V
return std::max(v, the_max2(b, ts...));

(3) вы использовали int тип для v, когда вы должны использовать auto (или typename T::value_type, если хотите)

// ...VVVV (not int)
const auto v = *std::max_element(t.cbegin(), t.cend());

(4) «общий тип» должен также оценивать Ts::value_type, поэтому

// ...........................................VVVVVVVVVVVVVVVVVVVVVVVVVV
std::common_type_t<B, typename T::value_type, typename Ts::value_type...>

(5) вы должны явно указать тип для std::max().

   using rt = std::common_type_t<B, typename T::value_type, typename Ts::value_type...>;
   // ...
   return std::max<rt>(v, the_max2(b, ts...));
   // ............^^^^

(6) Я предлагаю получать аргументы в виде константных указателей вместо значений r

 //..........VVVVVVV......VVVVVVV.......VVVVVVV
 the_max2 (B const & b, T const & t, Ts const & ... ts)

Ниже приведен полный пример компиляции (с упрощением для обнаружения только один раз возвращенного общего типа)

#include <array>
#include <iostream>
#include <algorithm>
#include <type_traits>

template <typename B>
constexpr std::enable_if_t<std::is_arithmetic<B>::value, B>
   the_max2 (B const & b)
 { return b; }


template <typename B, typename T, typename ... Ts,
          typename RT = std::common_type_t<B, typename T::value_type,
                                           typename Ts::value_type...>>
constexpr std::enable_if_t<std::is_arithmetic<B>::value
                        && std::is_arithmetic<typename T::value_type>::value, RT>
   the_max2 (B const & b, T const & t, Ts const & ... ts)
 {
   const auto v = *std::max_element(t.cbegin(), t.cend());
   return std::max<RT>(v, the_max2(b, ts...));
 }

int main()
 {
   constexpr std::array<short, 3> r1 {{1, 3, 5}};
   constexpr std::array<int, 2> r2 {{3, 4}};
   constexpr std::array<long, 4> r3 {{1, 2, 5, 6}};
   constexpr std::array<long long, 2> r4 {{2, 6}};

   auto  m { the_max2(4l, r1, r2, r3, r4) };

   std::cout << m << std::endl;
 }

Предложение бонуса: если вы можете отказаться от теста std::is_arithmetic, вам не нужна рекурсия, и вы можете написать свою функцию, просто расширив шаблон переменной следующим образом

template <typename B, typename ... Ts,
          typename RT = std::common_type_t<B, typename Ts::value_type...>>
constexpr RT the_max3 (B const & b, Ts const & ... ts)
 { return std::max<RT>({b, *std::max_element(ts.cbegin(), ts.cend())...}); }

Если вы можете использовать C ++ 17 вместо C ++ 14, вы можете использовать свертывание шаблонов для восстановления теста std::is_arithmetic SFINAE следующим образом

template <typename B, typename ... Ts,
          typename RT = std::common_type_t<B, typename Ts::value_type...>>
constexpr std::enable_if_t<
     (std::is_arithmetic<B>::value && ...
   && std::is_arithmetic<typename Ts::value_type>::value), RT>
   the_max3 (B const & b, Ts const & ... ts)
 { return std::max<RT>({b, *std::max_element(ts.cbegin(), ts.cend())...}); }
...