我想使用模板在两组类型之间生成类型安全的转换。基本情况如下:
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);
}
以上适用于基本类型。现在,我想将此扩展到指针和const限定类型。当我定义以下部分专业知识时,它将起作用:
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限定类型等定义此类型映射?
答案 0 :(得分:1)
将会有很多样板。
我实际上建议使用与模板专业化不同的系统。
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 *>);
其中decomp的类型
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);
}
同样,唯一的好处是,在系统中,将const,函数,数组等等全部弄得一团糟的事情一塌糊涂。您可以在其他地方重用它(在这种情况下,我使用了两次)。
假设我们也希望重新映射成员函数,那么将其扩展到成员函数时,情况自然会变得更糟(成员函数本身需要100个专业化知识才能涵盖一堆情况)。