я ищу шаблон преобразовать черты типа C ++ в их вариадные аналоги. методология подойти к проблеме будет оценена и генеративное программирование Шаблоны для автоматизации задачи были бы идеальными.
Возьмите следующее:
std::is_same<T, U>::value;
Я хочу написать черту, которая работает так:
std::are_same<T1, T2, T3, T4>::value;
Это довольно простой реализовать are_same
; Ищу общее решение мы можем придумать инструмент для реализации любой вариативной черты универсальное количественное определение :
template<template<class,class> class F, typename...Ts>
struct Univ;
template<template<class, class> class F, typename T, typename U, typename...Ts>
struct Univ<F, T, U, Ts...>
{
static const int value = F<T, U>::value && Univ<F, U, Ts...>::value;
};
template<template<class, class> class F, typename T>
struct Univ<F, T>
{
static const int value = 1;
};
так что например are_same
может быть написано как
Univ<is_same,int, int, int>::value
и это может применяться при создании таких черт, как are_classes
, are_scalars
так далее
Незначительные изменения могут дать экзистенциальная количественная оценка из предыдущего фрагмента (замена &&
с ||
) так что мы создаем такие черты, как exist_same
следующим образом:
Exist<is_same, int, double, float>::value
Предыдущее обобщение обложки на тип черты относится к
Как бы я обобщил для черт типа как следующее:
enable_if -> enable_if_any // enable if any clause is true
enable_if_all // enalbe if all clauses are true
enable_for // enable only for the type provided
exist_same
Пример выше упрощен. Есть идеи для правильной реализации?
Есть type_traits которые «возвращают» измененные типы. Любое предложение для масштабирования тех реализаций для произвольного числа типов?
Есть ли type_traits которые сделаны не масштабировать до произвольного числа аргументов типа ?
Я не совсем понимаю, чего именно вы хотели бы достичь, но следующие помощники могут быть полезны, начиная с bool_sequence
:
#include <type_traits>
// Note: std::integer_sequence is C++14,
// but it's easy to use your own version (even stripped down)
// for the following purpose:
template< bool... Bs >
using bool_sequence = std::integer_sequence< bool, Bs... >;
// Alternatively, not using C++14:
template< bool... > struct bool_sequence {};
Затем вы можете проверить, все ли или любое логическое значение или установить с помощью этих:
template< bool... Bs >
using bool_and = std::is_same< bool_sequence< Bs... >,
bool_sequence< ( Bs || true )... > >;
template< bool... Bs >
using bool_or = std::integral_constant< bool, !bool_and< !Bs... >::value >;
они пригодятся в качестве строительных блоков для более продвинутых и специализированных черт. Например, вы можете использовать их так:
typename< typename R, bool... Bs > // note: R first, no default :(
using enable_if_any = std::enable_if< bool_or< Bs... >::value, R >;
typename< typename R, bool... Bs > // note: R first, no default :(
using enable_if_all = std::enable_if< bool_and< Bs... >::value, R >;
typename< typename T, typename... Ts >
using are_same = bool_and< std::is_same< T, Ts >::value... >;
Вы также можете использовать std::conditional
чтобы достичь enable_if_all
а также enable_if_any
:
#include <type_traits>
#include <iostream>
#include <initializer_list>
#include <string>
namespace detail
{
template <typename... Conds>
struct and_ : std::true_type {};
template <typename... Conds>
struct or_ : std::false_type {};
template <typename Cond, typename... Conds>
struct and_<Cond, Conds...>
: std::conditional<Cond::value, detail::and_<Conds...>, std::false_type>::type {};
template <typename Cond, typename... Conds>
struct or_<Cond, Conds...>
: std::conditional<Cond::value, std::true_type, detail::and_<Conds...>>::type {};
}
template <typename... T>
using are_all_pod = detail::and_<std::is_pod<T>...>;
template <typename... T>
using any_is_pod = detail::or_<std::is_pod<T>...>;
template <typename... Args, typename = typename std::enable_if<are_all_pod<Args...>::value>::type>
void f(Args... args)
{
(void)std::initializer_list<int>{(std::cout << args << '\n' , 0)...};
}
template <typename... Args, typename = typename std::enable_if<any_is_pod<Args...>::value>::type>
void g(Args... args)
{
(void)std::initializer_list<int>{(std::cout << args << '\n' , 0)...};
}
int main()
{
std::string s = "hello"; // non pod
//f(1, 1.2, s); // this will fail because not all types are pod
g(1, 1.2, s); // this compiles because there is at least one pod in argument pack
}
Вдохновленные отличной идеей в ответе Дэниела Фрея, мы можем даже расширить сферу этих вариативные черты. Используя кортежи, мы можем применять признаки к коллекциям пакетов типов variadic вместо «только», сравнивая пакет типов variadic с ссылочным типом.
Например, мы сможем увидеть, если типы int, int, int, float
такие же типы, как int, int, int, float
(так и есть!)
Для этого нам понадобятся следующие конструкции:
Я собрал несколько примеров в этом живое демо.
Сначала мы предоставляем помощник для расширения последовательности bool по одному значению за раз:
template <bool ... Bs>
struct bool_sequence {};
template <bool b, typename T>
struct prepend_bool_seq;
template <bool b, bool ... bs>
struct prepend_bool_seq<b, bool_sequence<bs...>> {
typedef bool_sequence<b, bs...> type;
};
Теперь немного логики в последовательностях bool (взято из других ответов)
template <typename T>
struct all_of;
template <bool ... Bs>
struct all_of<bool_sequence<Bs...>> :
public std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>> {};
template <typename T>
struct any_of;
template <bool ... Bs>
struct any_of<bool_sequence<Bs...>> :
public std::integral_constant<bool, !all_of<bool_sequence<!Bs...>>::value> {};
Затем мы определяем вспомогательный шаблон для доступа к хвосту кортежа:
namespace details {
// Sentinel type to detect empty tuple tails
struct null_type {};
template <typename T>
struct tuple_tail;
template <typename T>
struct tuple_tail<std::tuple<T>> {
typedef null_type type;
};
template <typename T, typename ... Ts>
struct tuple_tail<std::tuple<T, Ts...>> {
typedef std::tuple<Ts...> type;
};
}
С помощью этих кубиков мы можем теперь определить apply_trait
шаблон для применения заданной черты типа к нескольким спискам типов:
namespace details {
template <template <typename...> class Trait, typename ... Tuples>
struct apply_trait {
static constexpr bool atomic_value =
Trait<typename std::tuple_element<0u, Tuples>::type...>::value;
typedef typename prepend_bool_seq<atomic_value,
typename apply_trait<Trait,
typename tuple_tail<Tuples>::type...>::type>::type type;
};
template <template <typename...> class Trait, typename ... Tuples>
struct apply_trait<Trait, null_type, Tuples...> {
typedef bool_sequence<> type;
};
}
Этот шаблон рекурсивно вычисляет последовательность bool, заданную приложением trait, снизу вверх. Теперь, имея полученную последовательность bool, мы можем выполнять логические операции над результатом с помощью помощников, определенных выше.
Далее, некоторые помощники могут воспроизвести логику вашего are_same
пример для любой черты двоичного (или унарного) типа:
// Helper templates for common type traits (unary and binary)
template <template <typename> class UnaryTrait, typename ... Ts>
using apply_unary_trait = details::apply_trait<UnaryTrait, std::tuple<Ts...>>;
template <template <typename, typename> class BinaryTrait, typename Ref, typename ... Ts>
using apply_binary_trait = details::apply_trait<BinaryTrait,
std::tuple<decltype(std::declval<Ts>(), std::declval<Ref>())...>,
std::tuple<Ts...>>;
template <template <typename, typename> class BinaryTrait, typename Ref, typename ... Ts>
using apply_binary_trait_ref_last = details::apply_trait<BinaryTrait,
std::tuple<Ts...>,
std::tuple<decltype(std::declval<Ts>(), std::declval<Ref>())...>>;
Например, мы можем воспроизвести are_same
дизайн, который вы воспитали для каждой бинарной черты:
template <typename Ref, typename ... Ts>
using are_same = all_of<typename apply_binary_trait<std::is_same, Ref, Ts...>::type>;
Мы также можем применить логику черт в списках. Например, учитывая два списка типов, мы можем захотеть проверить, можно ли преобразовать тип в первом списке в соответствующий ему тип во втором списке:
// int is convertible to long and char const* is convertible to std::string
std::cout << all_of<details::apply_trait<std::is_convertible,
std::tuple<int, char const*>,
std::tuple<long, std::string>::type>::value;