使用模板和部分专用化生成类型转换函数



我想使用模板在两组类型之间生成类型安全的转换。基本情况如下所示:

template <typename T> struct ATraits {};
template <typename T> struct BTraits {};
template <> struct ATraits<BFoo> { using AType = AFoo; };
template <> struct ATraits<BBar> { using AType = ABar; };
template <> struct BTraits<AFoo> { using BType = BFoo; };
template <> struct BTraits<ABar> { using BType = BBar; };
template <typename TB>
auto AFromB(TB x) -> typename ATraits<TB>::AType {
return static_cast<typename ATraits<TB>::AType>(x);
}
template <typename TA>
auto BFromA(TA x) -> typename BTraits<TA>::BType {
return static_cast<typename BTraits<TA>::BType>(x);
}

以上适用于基本类型。现在我想将其扩展到指针和常量限定类型。当我定义以下部分专业化时,它有效:

template <typename T> struct ATraits<T*> {
using AType = typename ATraits<T>::AType*;
}
template <typename T> struct BTraits<T*> {
using BType = typename BTraits<T>::BType*;
}
template <typename T> struct ATraits<const T> {
using AType = const typename ATraits<T>::AType;
}
template <typename T> struct BTraits<const T> {
using BType = const typename BTraits<T>::BType;
}

但是,这似乎是很多样板。是否有更简洁的方法(可能涉及类型特征)来为指针、引用、符合 cv 条件的类型等定义此类型映射?

会有很多样板。

我实际上建议使用与模板专业化不同的系统。

template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t=typename Tag::type;
struct const_t {}; constexpr const_t const_v{};
struct volatile_t {}; constexpr volatile_t volatile_v{};
struct ptr_t {}; constexpr ptr_t ptr_v{};
struct lref_t {}; constexpr lref_t lref_v{};
struct rref_t {}; constexpr rref_t rref_v{};
struct retval_t{}; constexpr retval_t retval_v{}; 
struct func_t{}; constexpr func_t func_v{};
template<class Sig>
struct func_builder_t{}; template<class Sig> constexpr func_builder_t<Sig> func_builder_v{};

现在是一个代数:

template<class T>
constexpr tag_t<T&> operator+( tag_t<T>,lref_t ) { return {}; }
template<class T>
constexpr tag_t<T&&> operator+( tag_t<T>,rref_t ) { return {}; }
template<class T>
constexpr tag_t<T*> operator+( tag_t<T>,ptr_t ) { return {}; }
template<class T>
constexpr tag_t<T const> operator+( tag_t<T>,const_t ) { return {}; }
template<class T>
constexpr tag_t<T volatile> operator+( tag_t<T>,volatile_t ) { return {}; }
template<class T>
constexpr func_builder_t<T()> operator+(tag_t<T>,retval_t){ return {}; }
template<class R, class...Ts, class T0, class T1>
constexpr func_builder_t<R(T1,Ts...,T0)> operator+(func_builder_t<R(T0,Ts...)>,tag_t<T1>){ return {}; }
template<class R, class T0>
constexpr func_builder_t<R(T0)> operator+(func_builder_t<R()>,tag_t<T0>){ return {}; }
template<class R, class...Ts, class T0>
constexpr tag_t<R(Ts...,T0)> operator+(func_builder_t<R(T0,Ts...)>,func_t){ return {}; }
template<class R, class...Ts, class T0, class Rhs>
constexpr auto operator+(func_builder_t<R(T0,Ts...)>,Rhs rhs){
return func_builder_v<R(Ts...)>+(tag<T0>+rhs);
}

接下来我们可以分解一些东西:

template<class T>
constexpr std::tuple<tag_t<T>> decompose( tag_t<T> ) { return {}; }
template<class T>
constexpr auto decompose( tag_t<T*> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( ptr_v ) );
}
template<class T>
constexpr auto decompose( tag_t<T&> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( lref_v ) );
}
template<class T>
constexpr auto decompose( tag_t<T&&> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( rref_v ) );
}
template<class T>
constexpr auto decompose( tag_t<T const> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( const_v ) );
}
template<class T>
constexpr auto decompose( tag_t<T volatile> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( volatile_v ) );
}

template<class T>
constexpr auto decompose( tag_t<T const volatile> ) {
return std::tuple_cat( decompose(tag<T>), std::make_tuple( const_v, volatile_v ) );
}
template<class R, class...Args>
constexpr auto decompose( tag_t<R(Args...)> ) {
constexpr auto args = std::tuple_cat( decompose(tag<Args>)... );
return std::tuple_cat( decompose(tag<R>), std::make_tuple(retval_v), args, std::make_tuple(func_v) );
}
template<class...Ts>
constexpr auto compose( std::tuple<Ts...> ) {
return (... + Ts{});
}

现在我们可以采用一种类型:

struct X;
tag<X * const volatile *>

并做

auto decomp0 = decompose(tag<X * const volatile *>);

其中分解类型为">

std::tuple< tag_t<X>, ptr_t, const_t, volatile_t, ptr_t > tup0 = decomp0;
auto decomp1 = decompose(tag<int(double, char)>);
std::tuple< tag_t<int>, retval_t, tag_t<double>, tag_t<char>, func_t > tup1 = decomp1;
tag_t<int(double, char)> tag_test = compose( decomp1 );
std::tuple< tag_t<int>, retval_t, tag_t<int>, func_t, ptr_t > tup_test_2 = decompose( tag<int(*)(int)> );
tag_t<int(*)(int)> tag_test_3 = compose( tup_test_2 );

我们可以更进一步,包括支持函数签名、大小和未大小、数组等。

然后我们在tag_t<T>上编写一个映射到我们想要的类型的函数。

接下来,我们分解传入类型,重新映射元组中的tag_t,然后使用折叠表达式和std::apply对元组求和。

但我疯了。

这样做的唯一好处是,您可以 (A) 重用分解/重组代码,并且 (B) 可以将类型映射分发到您正在使用的类型的命名空间,因为 tag_t 上的 map 函数将在标记类型的命名空间中查找函数名称。

活生生的例子。

然后,我们可以使用这种(相当复杂)的机器来解决您的问题。

template<class T>
constexpr auto ATypeFromB( tag_t<T> ) {
return tag< typename ATraits<T>::AType >;
}
template<class T>
constexpr auto BTypeFromA( tag_t<T> ) {
return tag< typename BTraits<T>::BType >;
}
template<class F, class T>
constexpr auto map_tags_only( F&& f, tag_t<T> t ) {
return f(t);
}
template<class F, class O>
constexpr auto map_tags_only( F&& f, O o ) {
return o;
}
template <typename TB>
auto AFromB(TB x) {
auto decomp = decompose( tag<TB> );
auto mapped = std::apply( [](auto...elements) {
return std::make_tuple(
map_tags_only( [](auto x){return ATypeFromB(x);}, elements )...
);
}, decomp );
auto comp = compose(mapped);
using R = typename decltype(comp)::type;
return static_cast<R>(x);
}  
template <typename TA>
auto BFromA(TA x) {
auto decomp = decompose( tag<TA> );
auto mapped = std::apply( [](auto...elements) {
return std::make_tuple(
map_tags_only( [](auto x){return BTypeFromA(x);}, elements )...
);
}, decomp );
auto comp = compose(mapped);
using R = typename decltype(comp)::type;
return static_cast<R>(x);
}  

活生生的例子。

同样,唯一的优点是,所有撕裂常量、函数、数组、等等的混乱在这个系统中都完成了一次。 你可以在其他地方重用它(在这种情况下,我用了两次)。

当我们将其扩展到成员函数(它本身需要 100 个专门化来涵盖一堆情况)时,情况自然会变得更糟,假设我们也想重新映射这些情况。

最新更新