我能想象到为类型转换器注册类型的最综合方式是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::tuples 中的几个列表是合成的,但难以理解和维护。
所以我提出了一种基于单个类型对列表的合成较少(但更易于理解和可维护)的方法
using list = std::tuple<std::pair<std::int16_t, float>,
std::pair<std::int32_t, double>,
std::pair<std::int64_t, long double>>;
现在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>> );
}