Будет много шаблонов.
Я бы фактически предложил использовать систему, отличную от специализации шаблонов.
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>
, которая соответствует типу, который мы хотим.
Далее мы разбиваем входящий тип, перераспределяем только тега_t в кортеже, затем суммируем кортеж с помощью выражения свертки и std::apply
.
Но я сумасшедший.
Единственное преимущество этого состоит в том, что вы можете (A) повторно использовать код декомпозиции / перекомпоновки и (B) можно распределять сопоставления типов по пространствам имен типов, с которыми вы работаете, как будет выглядеть функция map в tag_t вверх имя функции в пространстве имен тегового типа.
Живой пример .
Затем мы можем использовать этот (довольно сложный) механизм для решения вашей проблемы.
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 специализаций для охвата множества случаев), предполагая, что мы тоже хотим переназначить их.