Самым синтетическим способом регистрации типов для преобразователя типов является пара списков типов в std::tuple
(или что-то подобное).
Например: если вы хотите преобразоватьstd::int16_t
до float
, std::int32_t
до double
и std::int64_t
до long double
, вы можете using
определить пару типов
using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
using list2 = std::tuple<float, double, long double>;
Теперь, учитывая следующую структуру иобъявленная функция
template <typename, typename, typename>
struct foo
{ using type = std::tuple<>; };
template <typename T1, typename T2>
struct foo<T1, T1, T2>
{ using type = std::tuple<T2>; };
template <typename T, typename ... Ts1, typename ... Ts2>
constexpr auto bar (std::tuple<Ts1...>, std::tuple<Ts2...>)
-> decltype( std::tuple_cat(
std::declval<typename foo<T, Ts1, Ts2>::type>()...) );
TypeConverter
становится
template <typename T>
using TypeConverter
= std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(),
std::declval<list2>()))>;
Но я предполагаю, что пара списков в двух разных std::tuple
s является синтетической, но трудной для понимания и поддержки.
Итак, я предлагаю менее синтетический (но более понятный и поддерживаемый) способ, основанный на одном списке пар типов
using list = std::tuple<std::pair<std::int16_t, float>,
std::pair<std::int32_t, double>,
std::pair<std::int64_t, long double>>;
Now struct
и объявить функцию стать
template <typename, typename>
struct foo
{ using type = std::tuple<>; };
template <typename T1, typename T2>
struct foo<T1, std::pair<T1, T2>>
{ using type = std::tuple<T2>; };
template <typename T, typename ... Ts>
constexpr auto bar (std::tuple<Ts...>)
-> decltype( std::tuple_cat(
std::declval<typename foo<T, Ts>::type>()...) );
и TypeConverter
template <typename T>
using TypeConverter
= std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;
Ниже приведен пример полной компиляции C ++ 17 с обоими решениями (вы можете включить первое или второе, изменив #if 0
)
#include <tuple>
#include <type_traits>
#if 0
template <typename, typename, typename>
struct foo
{ using type = std::tuple<>; };
template <typename T1, typename T2>
struct foo<T1, T1, T2>
{ using type = std::tuple<T2>; };
template <typename T, typename ... Ts1, typename ... Ts2>
constexpr auto bar (std::tuple<Ts1...>, std::tuple<Ts2...>)
-> decltype( std::tuple_cat(
std::declval<typename foo<T, Ts1, Ts2>::type>()...) );
using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
using list2 = std::tuple<float, double, long double>;
template <typename T>
using TypeConverter
= std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(),
std::declval<list2>()))>;
#else
template <typename, typename>
struct foo
{ using type = std::tuple<>; };
template <typename T1, typename T2>
struct foo<T1, std::pair<T1, T2>>
{ using type = std::tuple<T2>; };
template <typename T, typename ... Ts>
constexpr auto bar (std::tuple<Ts...>)
-> decltype( std::tuple_cat(
std::declval<typename foo<T, Ts>::type>()...) );
using list = std::tuple<std::pair<std::int16_t, float>,
std::pair<std::int32_t, double>,
std::pair<std::int64_t, long double>>;
template <typename T>
using TypeConverter
= std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;
#endif
int main ()
{
static_assert( std::is_same_v<float, TypeConverter<std::int16_t>> );
static_assert( std::is_same_v<double, TypeConverter<std::int32_t>> );
static_assert( std::is_same_v<long double, TypeConverter<std::int64_t>> );
}