Вычисление функции на основе вариационных шаблонов

Я пытаюсь выяснить, какой самый идиоматичный способ реализовать функцию в списке типов переменных. Например, вычисление максимального размера всех типов. Я понимаю, что существует несколько подходов для решения такой задачи, но я хотел бы знать, когда выбирать, какую стратегию.

Вот механизмы, которые я бы рассмотрел (может быть, существует больше, пожалуйста, укажите, если так):

  • Тип черты (в идеале, кратко с использованием объявлений):

    template <typename Head>
    using max_size = typename std::integral_constant<size_t, sizeof(Head)>::type;
    
    template <typename Head, typename... Tail>
    using max_size = ?;
    
  • constexpr функции:

    template <typename Head>
    constexpr size_t max_size() { return sizeof(Head); }
    
    template <typename Head, typename... Tail>
    constexpr size_t max_size() { ? }
    

Мой вопрос двоякий:

  1. Какие особенности вычисления определяют, какую стратегию выбрать?

  2. Как будет выглядеть пример реализации для примера максимального размера, описанного выше?

3

Решение

Лично я предпочитаю функции, а не черты характера, я считаю, что ими легче манипулировать и они более естественны. Но это конечно субъективно;)

#include <iostream>

template <typename Head>
constexpr size_t max_size() { return sizeof(Head); }

template <typename Head, typename Next, typename... Tail>
constexpr size_t max_size() {
return max_size<Head>() > max_size<Next, Tail...>() ?
max_size<Head>() : max_size<Next, Tail...>();
}

int main() {
std::cout << "int: " << max_size<int>() << "\n";
std::cout << "char, short, int: " << max_size<char, short, int>() << "\n";
std::cout << "char, double, int: " << max_size<char, double, int>() << "\n";
}

В действии на liveworkspace:

int: 4
char, short, int: 4
char, double, int: 8
5

Другие решения

Я бы держался подальше от строго использования constexpr как их сложнее сочинять. Например, я даже не уверен, что метафункции высокого порядка возможны с constexpr, если это так, они используют указатели на функции в качестве параметров шаблона, что некрасиво.

В общем, я начинаю с класса метафункций:

struct max_size {
template<typename... Ts>
struct apply : parampack_foldl::apply<boost::mpl::quote2<boost::mpl::max>, typename boost::mpl::sizeof_<Ts>::type...>;
};

Затем создайте псевдоним, чтобы уменьшить ввод:

template<typename... Ts>
using MaxSize = typename max_size::apply<Ts>::type;

Или создайте функцию constexpr:

template <typename... Ts>
constexpr size_t max_size() { return max_size::apply<Ts...>::type::value; }

Второй шаг — это просто вопрос стиля. Что действительно важно, так это то, что у вас есть первый, который дает вам больше всего возможностей для работы.

3

Для полноты картины также существует метод наследования:

#include <cstddef>
using std::size_t;

template<size_t ...S> struct max_size_t;

template<size_t S1, size_t S2, size_t ...Rest>
struct max_size_t<S1, S2, Rest...>
: max_size_t<(S2 < S1) ? S1 : S2, Rest...> {
};

template<size_t S>
struct max_size_t<S> {
static const int value = S;
};

template<> struct max_size_t<> {
static const int value = 0;
};

template<typename ...T> struct max_size : max_size_t<sizeof(T)...> {};

// Using the same test-harness as Matthieu M:
#include <iostream>

int main() {
std::cout << "int: " << max_size<int>::value << "\n";
std::cout << "char, short, int: " << max_size<char, short, int>::value << "\n";
std::cout << "char, double, int: " << max_size<char, double, int>::value << "\n";
return 0;
}

Также в liveworkspace.

Хотя это не способ, который я бы выбрал для реализации max_size, это очень удобно, когда искомая функция возвращает функцию (пример очень хитрый):

template<typename T1, typename T2, bool B=(sizeof(T1)>sizeof(T2))> struct selector;
template<typename T1, typename T2> struct selector<T1, T2, true> { using type = T1; };
template<typename T1, typename T2> struct selector<T1, T2, false> { using type = T2; };

template<typename T1, typename ...Rest> struct largest_type;

template<typename T1, typename T2, typename ...Rest>
struct largest_type<T1, T2, Rest...>
: largest_type<typename selector<T1, T2>::type, Rest...> {};

template<typename T1> struct largest_type<T1> { using type = T1; };

#include <iostream>
int main() {
static const unsigned long long u = 1ULL << 63;
std::cout << "int: " << typename largest_type<int>::type(u) << "\n";
std::cout << "int, double: " << typename largest_type<int, double>::type(u) << "\n";
std::cout << "short, float, long long: " << typename largest_type<short, float, long long>::type(u) << "\n";
return 0;
}

Видеть это Вот.

1
По вопросам рекламы [email protected]