元编程技巧:如何简化两个元函数的实现
Metaprogramming tricks: how to simplify implementation of two metafunctions
我正在编写一些程序来通过代码生成自动调用一些 API。
在某些情况下,我需要从 Source
类型转换为 Target
类型,但这些类型带有指针、const 等装饰。所以我需要做的是删除所有装饰,例如指针、常量、数组等,获取普通类型以将其映射到另一种类型,然后将装饰应用回新类型。
该实现有很多模板专业化。代码后的问题。我不能使用 constexpr
元编程,因为我需要让它与 VS2013.
一起工作
template <class T>
struct TypeIs {
using type = T;
};
template <class T>
struct GetPlainType : TypeIs<typename std::decay<T>::type> {};
template <class T>
struct GetPlainType<T&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T*> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const *> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T[I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T const [I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
using GetPlainType_t = typename GetPlainType<T>::type;
template <class Decorated, class Plain>
struct CopyDecorations : TypeIs<Plain> {};
template <class T, class Plain>
struct CopyDecorations<T const, Plain> :
TypeIs<typename CopyDecorations<T, Plain const>::type> {};
template <class T, class Plain>
struct CopyDecorations<T *, Plain> :
TypeIs<typename CopyDecorations<T, Plain *>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const *, Plain> :
TypeIs<typename CopyDecorations<T, Plain const *>::type> {};
template <class T, class Plain>
struct CopyDecorations<T &, Plain> :
TypeIs<typename CopyDecorations<T, Plain &>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const &, Plain> :
TypeIs<typename CopyDecorations<T, Plain const &>::type> {};
template <class T, class Plain>
struct CopyDecorations<T &&, Plain> :
TypeIs<typename CopyDecorations<T, Plain &&>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const &&, Plain> :
TypeIs<typename CopyDecorations<T, Plain const &&>::type> {};
template <class T, class Plain>
struct CopyDecorations<T[], Plain> :
TypeIs<typename CopyDecorations<T, Plain[]>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const [], Plain> :
TypeIs<typename CopyDecorations<T, Plain const []>::type> {};
template <class T, class Plain, std::size_t I>
struct CopyDecorations<T [I], Plain> :
TypeIs<typename CopyDecorations<T, Plain[I]>::type> {};
template <class T, class Plain, std::size_t I>
struct CopyDecorations<T const [I], Plain> :
TypeIs<typename CopyDecorations<T, Plain const [I]>::type> {};
template <class Decorated, class Plain>
using CopyDecorations_t = typename CopyDecorations<Decorated, Plain>::type;
int main()
{
static_assert(std::is_same<GetPlainType_t<int>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int *>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int **>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int * &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int ** &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const * []>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const **[][3][5]>, int>{}, "");
static_assert(std::is_same<CopyDecorations_t<int, double>, double>{}, "");
static_assert(std::is_same<CopyDecorations_t<int const, double>, double const>{}, "");
static_assert(std::is_same<CopyDecorations_t<int *, double>, double *>{}, "");
static_assert(std::is_same<CopyDecorations_t<int **, double>, double **>{}, "");
static_assert(std::is_same<CopyDecorations_t<int[], double>, double[]>{}, "");
static_assert(std::is_same<CopyDecorations_t<int[3], double>, double[3]>{}, "");
//******************THE TESTS BELOW DO NOT WORK
//static_assert(std::is_same<CopyDecorations_t<int[][3], double>, double[][3]>{}, "");
//static_assert(std::is_same<CopyDecorations_t<int * &, double>, double * &>{}, "");
// static_assert
// (
//std::is_same<CopyDecorations_t<int const * [], double>,
// double const * []>{}, "");
// static_assert
// (std::is_same<CopyDecorations_t<int const **[][3][5], double>,
// double const **[][3][5]>{}, "");
}
问题:
- 我可以简化实施吗?
- 失败的测试(参见
main
函数),我该如何修复它们?
- 在哪些情况下(忽略 volatile 和指向成员的指针、指向函数的指针和函数)。你认为我的实施会失败吗?
我喜欢用标签进行类型元编程。
enum type_quals {
none_qualified = 0,
const_qualified = 1,
volatile_qualified = 2,
lreference_qualified = 4,
rreference_qualified = 8,
pointer_qualified = 16,
all_qualified = 31,
};
template<type_quals qs, class inside=void>
struct tq_t {
constexpr tq() {};
constexpr explicit operator bool() const { return qs!=none_qualified; }
};
template<type_quals q>
tq_t<q> tq{};
template<type_quals a, type_quals b>
constexpr
tq_t< type_quals(unsigned(a)|unsigned(b)) >
operator|( tq_t<a>, tq_t<b> ) { return {}; }
template<type_quals a, type_quals b>
constexpr
tq_t< type_quals(a&b) >
operator&( tq_t<a>, ta_t<b> ) { return {}; }
template<class T>
struct tag_t {
constexpr tag_t() {};
using type=T;
};
template<class T>
tag_t<T> tag{};
template<class T>
constexpr
tag_t<const T>
operator+( tag_t<T>, tq_t<const_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<volatile T>
operator+( tag_t<T>, tq_t<volatile_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T&>
operator+( tag_t<T>, tq_t<lreference_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T&&>
operator+( tag_t<T>, tq_t<rreference_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T>
operator+( tag_t<T>, tq_t<none_qualified> ) { return {}; }
template<class T, type_quals qs>
constexpr
auto
operator+( tag_t<T> t, tq_t<qs> q ) {
return t
+(q&tq<const_qualified>)
+(q&tq<volatile_qualified>)
+(q&tq<lreference_qualified>)
+(q&tq<rreference_qualified>)
;
}
template<class T, type_quals qs>
constexpr
auto
operator+( tq_t<qs> q, tag_t<T> t ) {
return t+q;
}
现在一旦你有了一个 tq 和一个标签,你就可以做加法以将类型添加回标签。
指针需要嵌套,因此是另一种tq
,但模式有些相似。以上暂不支持嵌套
您现在需要代码来检测 tag
中的 tq
并删除它们。也许添加一个 tq&tag
运算符,即 returns 标签上的 tq
。然后添加 tag-tq
以从 tag
.
中删除匹配的 tq
您的代码最终看起来像:
auto decorations = tag<T>&tq<all_qualified>;
auto raw = tag<T>-decorations;
// use raw::type here for the undecorated type
// produce type R
auto redecorated = tag<R>+decorations;
return redecorated;
现在,所有这一切真正做的是将烦人的类型的东西从业务逻辑中移出。但它以一种漂亮的方式做到了。
我发现这个问题确实是关于 SO 上的 C++ 元编程最有趣的问题之一。
我喜欢尝试找到合适的 解决方案 。谢谢你。 :-)
它遵循一个最小的工作示例。
它不是完整的,但它给出了一种可能的方法来实现这一点。
函数 f
(好吧,你可以在代码中选择一个更好的名字)接受两个模板参数:要 cleaned 的类型和要 的类型装饰.
它 return 是一个模板类型 (types
),引入了两个 using 声明,basic
和 decorated
,第一个模板参数清理为 basic
,第二个一个装饰为 decorated
.
它一次完成所有工作(清理和装饰)。您仍然可以只使用第一个参数,在这种情况下 decorated
默认为修饰的 char
类型。
完整代码如下:
#include<type_traits>
#include<cstddef>
static constexpr std::size_t N = 42;
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<typename T, typename U>
struct types {
using basic = T;
using decorated = U;
};
template<typename T, typename U>
constexpr auto
f(choice<0>) { return types<T, U>{}; }
template<typename T, typename U,
typename = std::enable_if_t<std::is_pointer<T>::value>>
constexpr auto f(choice<1>) {
auto t = f<std::remove_pointer_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_pointer_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_lvalue_reference<T>::value>>
constexpr auto f(choice<2>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_lvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
constexpr auto f(choice<3>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_rvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_array<T>::value>>
constexpr auto f(choice<4>) {
auto t = f<std::remove_extent_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::conditional_t<(0==std::extent<T>::value), D[], D[std::extent<T>::value]>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_const<T>::value>>
constexpr auto f(choice<5>) {
auto t = f<std::remove_const_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_const_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_volatile<T>::value>>
constexpr auto f(choice<6>) {
auto t = f<std::remove_volatile_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_volatile_t<D>>{};
}
template<typename T, typename U = char>
constexpr auto f() {
return f<T, U>(choice<N>{});
}
int main() {
// something complex to show that it seems to work
static_assert(std::is_same<
decltype(f<const int ** const &&, char>()),
types<int, const char ** const &&>
>::value, "!");
// some of the OP's examples (the most interesting)
static_assert(std::is_same<decltype(f<int, int>()), types<int, int>>::value, "!");
static_assert(std::is_same<decltype(f<int const, int>()), types<int, int const>>::value, "!");
static_assert(std::is_same<decltype(f<int *, int>()), types<int, int *>>::value, "!");
static_assert(std::is_same<decltype(f<int **, double>()), types<int, double **>>::value, "!");
static_assert(std::is_same<decltype(f<int *&, int>()), types<int, int *&>>::value, "!");
static_assert(std::is_same<decltype(f<int **&, float>()), types<int, float **&>>::value, "!");
static_assert(std::is_same<decltype(f<int [3], char>()), types<int, char [3]>>::value, "!");
static_assert(std::is_same<decltype(f<int [], int>()), types<int, int []>>::value, "!");
static_assert(std::is_same<decltype(f<int [][3], double>()), types<int, double [][3]>>::value, "!");
static_assert(std::is_same<decltype(f<int const **[][3][5], int>()), types<int, int const **[][3][5]>>::value, "!");
// of course, you don't need to provide the second type if you don't need it
// in this case, types::decorated is defaulted to a decorated char type
f<int const **[][3][5]>();
}
由于 static_assert
s 而没有 constexpr
s 就无法编译这一事实,您可以自由删除它们并在运行时使用该函数。
实际上,它可能会变成 无定义 解决方案,为声明提供正确的 return 类型并使用一堆 decltype
s,但我怀疑它远非可读性。
编辑
如 OP 所述,他不想(或者至少,他不能使用)constexpr
s。
它遵循了一个略有不同的解决方案,仍然基于之前的解决方案。
基本思想是使用 f
作为未评估的操作数 decltype
.
这是完整的代码:
#include<type_traits>
#include<cstddef>
static const std::size_t N = 42;
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<typename T, typename U>
struct types {
using basic = T;
using decorated = U;
};
template<typename T, typename U>
auto f(choice<0>) { return types<T, U>{}; }
template<typename T, typename U,
typename = std::enable_if_t<std::is_pointer<T>::value>>
auto f(choice<1>) {
auto t = f<std::remove_pointer_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_pointer_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_lvalue_reference<T>::value>>
auto f(choice<2>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_lvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
auto f(choice<3>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_rvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_array<T>::value>>
auto f(choice<4>) {
auto t = f<std::remove_extent_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::conditional_t<(0==std::extent<T>::value), D[], D[std::extent<T>::value]>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_const<T>::value>>
auto f(choice<5>) {
auto t = f<std::remove_const_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_const_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_volatile<T>::value>>
auto f(choice<6>) {
auto t = f<std::remove_volatile_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_volatile_t<D>>{};
}
template<typename T, typename U>
auto f() {
return f<T, U>(choice<N>{});
}
template<typename T, typename U = char>
using my_type = decltype(f<T, U>());
template<typename T, typename U = char>
using my_type_basic_t = typename decltype(f<T, U>())::basic;
template<typename T, typename U = char>
using my_type_decorated_t = typename decltype(f<T, U>())::decorated;
int main() {
int i = 42;
my_type_decorated_t<char *, int> ptr = &i;
// of course, it can still be used in a constant expression if needed
// constexpr my_type_decorated_t<char *, int> ptr = nullptr;
}
因此,您可以使用模式匹配并执行转录的一个步骤的函数来执行此操作,如下所示:
template<class In, class Out>
struct types {
using type=types;
using in=In;
using out=Out;
};
// transcribe cv:
template<class In, class Out>
types<In, const Out> transcribe( types<const In, Out> ) { return {}; }
template<class In, class Out>
types<In, volatile Out> transcribe( types<volatile In, Out> ) { return {}; }
template<class In, class Out>
types<In, const volatile Out> transcribe( types<const volatile In, Out> ) { return {}; }
// references and pointers:
template<class In, class Out>
types<In, Out*> transcribe( types<In*, Out> ) { return {}; }
template<class In, class Out>
types<In, Out&> transcribe( types<In&, Out> ) { return {}; }
template<class In, class Out>
types<In, Out&&> transcribe( types<In&&, Out> ) { return {}; }
// arrays
template<class In, class Out>
types<In, Out[]> transcribe( types<In[], Out> ) { return {}; }
template<class In, class Out, std::size_t N>
types<In, Out[N]> transcribe( types<In[N], Out> ) { return {}; }
// return type of a function
template<class In, class...In_Args, class Out>
types<In, Out(In_Args...)> transcribe( types<In(In_Args...), Out> ) { return {}; }
// return type of a function
template<class In, class...In_Args, class Out>
types<In, Out(*)(In_Args...)> transcribe( types<In(*)(In_Args...), Out> ) { return {}; }
// default case
template<class X>
X transcribe( X ) { return {}; }
超载规则做正确的事。唯一令人讨厌的是 const volatile
案例。
一旦我们有了上面的内容,我们就可以把它变成一个特征:
template<class In, class Out>
struct transcribe_one:
decltype(transcribe( types<In,Out>{} ))
{};
很容易。请注意,永远不需要调用 transcribe
。
别名使它更易于使用:
template<class T>
using strip_one=typename transcribe_one<T, int>::in;
template<class T>
using can_strip=std::integral_constant<bool, !std::is_same<T, strip_one<T>>{}>;
template<class T, class U>
using typescribe_one=typename transcribe_one<T, U>::out;
也表示"is there anything to strip?".
这只会从左到右移动一种类型的装饰。要移动它们,我们只需这样做:
template<class In, class Out, class=void>
struct transcribe_all:types<In, Out> {};
template<class T>
using strip=typename transcribe_all<T, int>::in;
template<class T, class U>
using typescribe=typename transcribe_all<T, U>::out;
template<class In, class Out>
struct transcribe_all<In, Out, std::enable_if_t<
can_strip<In>{}
>> :
types<
strip< strip_one< In > >, // must strip on strip_one, trust me
typescribe_one<
In,
typescribe< strip_one<In>, Out >
>
>
{};
当你不能剥离时,它什么都不做。
当您可以剥离时,它会从 In 类型中剥离一个,将其余部分转录到 Out 上,然后在其结果上进行一步转录。
这给你两个别名:
template<class T>
using strip=// ...
template<class T, class U>
using typescribe=// ...
第一个采用类型 T
并将其剥离到底部的 "raw" 类型。
第二个采用类型 T
并将其所有装饰移动到 U
。
template<template<class...>class M, class U>
using under_map = typescribe< U, M<strip<U>> >;
剥离类型装饰,应用 M
,然后重新应用它们。
请注意,最多 "user friendly" 的工具是 strip<T>
和 typescribe<In, Out>
。第一个删除所有装饰器并获得 "underlying" 类型。第二个将装饰器从一种类型复制到另一种类型。使用 transcribe_all
或 transcribe_one
或 strip_one
等可能会导致混淆,它们是实现细节。
只需将类型映射写为 template
并将其传递给 under_map
。
唯一使用的 C++14 功能是 std::enable_if_t<?>
,在 C++11 上可以用 typename std::enable_if<?>::type
代替。
MSVC 可能对 decltype
的东西有问题,因为它对 SFINAE 中的 decltype 的支持很糟糕。
这是我的解决方案。它可以满足我的需要(无易失性)。
template <class T>
struct TypeIs {
using type = T;
};
template <class T>
struct GetPlainType : TypeIs<typename std::decay<T>::type> {};
template <class T>
struct GetPlainType<T const> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T*> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const *> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T[I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T const [I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
using GetPlainType_t = typename GetPlainType<T>::type;
namespace detail {
//Qualifiers
struct ConstQual {};
//Category
struct ValueCat {};
template <std::size_t I = 0>
struct ArrayCat : std::integral_constant<std::size_t, I> {};
struct PointerCat {};
struct LValueReferenceCat {};
struct RValueReferenceCat {};
template <class Cat, class...Quals>
struct Decoration {
using Category = Cat;
using Qualifiers = std::tuple<Quals...>;
};
template <class Cat, class...Quals>
using DecorationCategory_t = typename Decoration<Cat, Quals...>::type;
template <class T>
struct SaveDecorations : TypeIs<brigand::list<Decoration<ValueCat>>> {};
template <class T>
struct SaveDecorations<T const> : TypeIs<brigand::list<Decoration<ValueCat, ConstQual>>> {};
template <class T>
struct SaveDecorations<T *> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<PointerCat>>>> {};
template <class T>
struct SaveDecorations<T * const> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<PointerCat, ConstQual>>>> {};
template <class T>
struct SaveDecorations<T &> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<LValueReferenceCat>>>> {};
template <class T>
struct SaveDecorations<T &&> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<RValueReferenceCat>>>> {};
template <class T>
struct SaveDecorations<T []> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<ArrayCat<>>>
>> {};
template <class T>
struct SaveDecorations<T const []> :
TypeIs<
brigand::append<
typename SaveDecorations<T const>::type,
brigand::list<Decoration<ArrayCat<>>>
>> {};
template <class T, std::size_t N>
struct SaveDecorations<T [N]> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<ArrayCat<N>>>
>> {};
template <class T, std::size_t N>
struct SaveDecorations<T const [N]> :
TypeIs<
brigand::append<
typename SaveDecorations<T const>::type,
brigand::list<Decoration<ArrayCat<N>>>
>> {};
template <class State, class Elem>
struct AddDecoration : TypeIs<State> {};
template <class State>
struct AddDecoration<State, Decoration<ValueCat, ConstQual>> : TypeIs<State const> {};
template <class State>
struct AddDecoration<State, Decoration<PointerCat>> : TypeIs<State *> {};
template <class State>
struct AddDecoration<State, Decoration<PointerCat, ConstQual>> :
TypeIs<State * const> {};
template <class State>
struct AddDecoration<State, Decoration<LValueReferenceCat>> : TypeIs<State &> {};
template <class State>
struct AddDecoration<State, Decoration<RValueReferenceCat>> : TypeIs<State &&> {};
template <class State>
struct AddDecoration<State, Decoration<ArrayCat<>>> : TypeIs<State[]> {};
template <class State, std::size_t I>
struct AddDecoration<State, Decoration<ArrayCat<I>>> : TypeIs<State[I]> {};
template <class T, class DecorationsList>
struct ApplyDecorations :
TypeIs<brigand::fold<DecorationsList, T,
AddDecoration<brigand::_state,
brigand::_element>>> {};
}
template <class T>
using SaveDecorations_t = typename detail::SaveDecorations<T>::type;
template <class T, class TList>
using ApplyDecorations_t = typename detail::ApplyDecorations<T, TList>::type;
int main()
{
static_assert(std::is_same<GetPlainType_t<int>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int *>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int **>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int * &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int ** &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const * []>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const **[][3][5]>, int>{}, "");
using Decorations = SaveDecorations_t<int>;
using shouldBeFloat = ApplyDecorations_t<float, Decorations>;
static_assert(std::is_same<float, shouldBeFloat>{}, "");
using Decorations2 = SaveDecorations_t<int const>;
using shouldBeConst = ApplyDecorations_t<float, Decorations2>;
static_assert(std::is_same<shouldBeConst, float const>{}, "");
using Decorations3 = SaveDecorations_t<int const *>;
using shouldPointerToConst = ApplyDecorations_t<float, Decorations3>;
static_assert(std::is_same<shouldPointerToConst, float const *>{}, "");
using Decorations4 = SaveDecorations_t<int const * const>;
using shouldConstPointerToConst = ApplyDecorations_t<float, Decorations4>;
static_assert(std::is_same<shouldConstPointerToConst, float const * const>{}, "");
using Decorations5 = SaveDecorations_t<int const * const &>;
using shouldBeLValRefToConstPointerToConst = ApplyDecorations_t<float, Decorations5>;
static_assert(std::is_same<shouldBeLValRefToConstPointerToConst, float const * const &>{}, "");
using Decorations6 = SaveDecorations_t<int * const ** const &>;
using Res = ApplyDecorations_t<float, Decorations6>;
static_assert(std::is_same<Res, float * const ** const &>{}, "");
using Decorations7 = SaveDecorations_t<int * const>;
using Res2 = ApplyDecorations_t<float, Decorations7>;
static_assert(std::is_same<Res2, float * const>{}, "");
//Arrays tests
using Decorations8 = SaveDecorations_t<int const * const * const []>;
using Res3 = ApplyDecorations_t<float, Decorations8>;
static_assert(std::is_same<Res3, float const * const * const []>{}, "");
using Decorations9 = SaveDecorations_t<int const * const * [3]>;
using Res4 = ApplyDecorations_t<float, Decorations9>;
static_assert(std::is_same<Res4, float const * const * [3]>{}, "");
//Multidimensional arrays
using Decorations10 = SaveDecorations_t<int const * const * [3][5]>;
using Res5 = ApplyDecorations_t<float, Decorations10>;
static_assert(std::is_same<Res5, float const * const * [3][5]>{}, "");
using Decorations11 = SaveDecorations_t<int const * const * [][3][5]>;
using Res6 = ApplyDecorations_t<float, Decorations11>;
static_assert(std::is_same<Res6, float const * const * [][3][5]>{}, "");
}
我正在编写一些程序来通过代码生成自动调用一些 API。
在某些情况下,我需要从 Source
类型转换为 Target
类型,但这些类型带有指针、const 等装饰。所以我需要做的是删除所有装饰,例如指针、常量、数组等,获取普通类型以将其映射到另一种类型,然后将装饰应用回新类型。
该实现有很多模板专业化。代码后的问题。我不能使用 constexpr
元编程,因为我需要让它与 VS2013.
template <class T>
struct TypeIs {
using type = T;
};
template <class T>
struct GetPlainType : TypeIs<typename std::decay<T>::type> {};
template <class T>
struct GetPlainType<T&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T*> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const *> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T[I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T const [I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
using GetPlainType_t = typename GetPlainType<T>::type;
template <class Decorated, class Plain>
struct CopyDecorations : TypeIs<Plain> {};
template <class T, class Plain>
struct CopyDecorations<T const, Plain> :
TypeIs<typename CopyDecorations<T, Plain const>::type> {};
template <class T, class Plain>
struct CopyDecorations<T *, Plain> :
TypeIs<typename CopyDecorations<T, Plain *>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const *, Plain> :
TypeIs<typename CopyDecorations<T, Plain const *>::type> {};
template <class T, class Plain>
struct CopyDecorations<T &, Plain> :
TypeIs<typename CopyDecorations<T, Plain &>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const &, Plain> :
TypeIs<typename CopyDecorations<T, Plain const &>::type> {};
template <class T, class Plain>
struct CopyDecorations<T &&, Plain> :
TypeIs<typename CopyDecorations<T, Plain &&>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const &&, Plain> :
TypeIs<typename CopyDecorations<T, Plain const &&>::type> {};
template <class T, class Plain>
struct CopyDecorations<T[], Plain> :
TypeIs<typename CopyDecorations<T, Plain[]>::type> {};
template <class T, class Plain>
struct CopyDecorations<T const [], Plain> :
TypeIs<typename CopyDecorations<T, Plain const []>::type> {};
template <class T, class Plain, std::size_t I>
struct CopyDecorations<T [I], Plain> :
TypeIs<typename CopyDecorations<T, Plain[I]>::type> {};
template <class T, class Plain, std::size_t I>
struct CopyDecorations<T const [I], Plain> :
TypeIs<typename CopyDecorations<T, Plain const [I]>::type> {};
template <class Decorated, class Plain>
using CopyDecorations_t = typename CopyDecorations<Decorated, Plain>::type;
int main()
{
static_assert(std::is_same<GetPlainType_t<int>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int *>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int **>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int * &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int ** &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const * []>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const **[][3][5]>, int>{}, "");
static_assert(std::is_same<CopyDecorations_t<int, double>, double>{}, "");
static_assert(std::is_same<CopyDecorations_t<int const, double>, double const>{}, "");
static_assert(std::is_same<CopyDecorations_t<int *, double>, double *>{}, "");
static_assert(std::is_same<CopyDecorations_t<int **, double>, double **>{}, "");
static_assert(std::is_same<CopyDecorations_t<int[], double>, double[]>{}, "");
static_assert(std::is_same<CopyDecorations_t<int[3], double>, double[3]>{}, "");
//******************THE TESTS BELOW DO NOT WORK
//static_assert(std::is_same<CopyDecorations_t<int[][3], double>, double[][3]>{}, "");
//static_assert(std::is_same<CopyDecorations_t<int * &, double>, double * &>{}, "");
// static_assert
// (
//std::is_same<CopyDecorations_t<int const * [], double>,
// double const * []>{}, "");
// static_assert
// (std::is_same<CopyDecorations_t<int const **[][3][5], double>,
// double const **[][3][5]>{}, "");
}
问题:
- 我可以简化实施吗?
- 失败的测试(参见
main
函数),我该如何修复它们? - 在哪些情况下(忽略 volatile 和指向成员的指针、指向函数的指针和函数)。你认为我的实施会失败吗?
我喜欢用标签进行类型元编程。
enum type_quals {
none_qualified = 0,
const_qualified = 1,
volatile_qualified = 2,
lreference_qualified = 4,
rreference_qualified = 8,
pointer_qualified = 16,
all_qualified = 31,
};
template<type_quals qs, class inside=void>
struct tq_t {
constexpr tq() {};
constexpr explicit operator bool() const { return qs!=none_qualified; }
};
template<type_quals q>
tq_t<q> tq{};
template<type_quals a, type_quals b>
constexpr
tq_t< type_quals(unsigned(a)|unsigned(b)) >
operator|( tq_t<a>, tq_t<b> ) { return {}; }
template<type_quals a, type_quals b>
constexpr
tq_t< type_quals(a&b) >
operator&( tq_t<a>, ta_t<b> ) { return {}; }
template<class T>
struct tag_t {
constexpr tag_t() {};
using type=T;
};
template<class T>
tag_t<T> tag{};
template<class T>
constexpr
tag_t<const T>
operator+( tag_t<T>, tq_t<const_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<volatile T>
operator+( tag_t<T>, tq_t<volatile_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T&>
operator+( tag_t<T>, tq_t<lreference_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T&&>
operator+( tag_t<T>, tq_t<rreference_qualified> ) { return {}; }
template<class T>
constexpr
tag_t<T>
operator+( tag_t<T>, tq_t<none_qualified> ) { return {}; }
template<class T, type_quals qs>
constexpr
auto
operator+( tag_t<T> t, tq_t<qs> q ) {
return t
+(q&tq<const_qualified>)
+(q&tq<volatile_qualified>)
+(q&tq<lreference_qualified>)
+(q&tq<rreference_qualified>)
;
}
template<class T, type_quals qs>
constexpr
auto
operator+( tq_t<qs> q, tag_t<T> t ) {
return t+q;
}
现在一旦你有了一个 tq 和一个标签,你就可以做加法以将类型添加回标签。
指针需要嵌套,因此是另一种tq
,但模式有些相似。以上暂不支持嵌套
您现在需要代码来检测 tag
中的 tq
并删除它们。也许添加一个 tq&tag
运算符,即 returns 标签上的 tq
。然后添加 tag-tq
以从 tag
.
tq
您的代码最终看起来像:
auto decorations = tag<T>&tq<all_qualified>;
auto raw = tag<T>-decorations;
// use raw::type here for the undecorated type
// produce type R
auto redecorated = tag<R>+decorations;
return redecorated;
现在,所有这一切真正做的是将烦人的类型的东西从业务逻辑中移出。但它以一种漂亮的方式做到了。
我发现这个问题确实是关于 SO 上的 C++ 元编程最有趣的问题之一。
我喜欢尝试找到合适的 解决方案 。谢谢你。 :-)
它遵循一个最小的工作示例。
它不是完整的,但它给出了一种可能的方法来实现这一点。
函数 f
(好吧,你可以在代码中选择一个更好的名字)接受两个模板参数:要 cleaned 的类型和要 的类型装饰.
它 return 是一个模板类型 (types
),引入了两个 using 声明,basic
和 decorated
,第一个模板参数清理为 basic
,第二个一个装饰为 decorated
.
它一次完成所有工作(清理和装饰)。您仍然可以只使用第一个参数,在这种情况下 decorated
默认为修饰的 char
类型。
完整代码如下:
#include<type_traits>
#include<cstddef>
static constexpr std::size_t N = 42;
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<typename T, typename U>
struct types {
using basic = T;
using decorated = U;
};
template<typename T, typename U>
constexpr auto
f(choice<0>) { return types<T, U>{}; }
template<typename T, typename U,
typename = std::enable_if_t<std::is_pointer<T>::value>>
constexpr auto f(choice<1>) {
auto t = f<std::remove_pointer_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_pointer_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_lvalue_reference<T>::value>>
constexpr auto f(choice<2>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_lvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
constexpr auto f(choice<3>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_rvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_array<T>::value>>
constexpr auto f(choice<4>) {
auto t = f<std::remove_extent_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::conditional_t<(0==std::extent<T>::value), D[], D[std::extent<T>::value]>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_const<T>::value>>
constexpr auto f(choice<5>) {
auto t = f<std::remove_const_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_const_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_volatile<T>::value>>
constexpr auto f(choice<6>) {
auto t = f<std::remove_volatile_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_volatile_t<D>>{};
}
template<typename T, typename U = char>
constexpr auto f() {
return f<T, U>(choice<N>{});
}
int main() {
// something complex to show that it seems to work
static_assert(std::is_same<
decltype(f<const int ** const &&, char>()),
types<int, const char ** const &&>
>::value, "!");
// some of the OP's examples (the most interesting)
static_assert(std::is_same<decltype(f<int, int>()), types<int, int>>::value, "!");
static_assert(std::is_same<decltype(f<int const, int>()), types<int, int const>>::value, "!");
static_assert(std::is_same<decltype(f<int *, int>()), types<int, int *>>::value, "!");
static_assert(std::is_same<decltype(f<int **, double>()), types<int, double **>>::value, "!");
static_assert(std::is_same<decltype(f<int *&, int>()), types<int, int *&>>::value, "!");
static_assert(std::is_same<decltype(f<int **&, float>()), types<int, float **&>>::value, "!");
static_assert(std::is_same<decltype(f<int [3], char>()), types<int, char [3]>>::value, "!");
static_assert(std::is_same<decltype(f<int [], int>()), types<int, int []>>::value, "!");
static_assert(std::is_same<decltype(f<int [][3], double>()), types<int, double [][3]>>::value, "!");
static_assert(std::is_same<decltype(f<int const **[][3][5], int>()), types<int, int const **[][3][5]>>::value, "!");
// of course, you don't need to provide the second type if you don't need it
// in this case, types::decorated is defaulted to a decorated char type
f<int const **[][3][5]>();
}
由于 static_assert
s 而没有 constexpr
s 就无法编译这一事实,您可以自由删除它们并在运行时使用该函数。
实际上,它可能会变成 无定义 解决方案,为声明提供正确的 return 类型并使用一堆 decltype
s,但我怀疑它远非可读性。
编辑
如 OP 所述,他不想(或者至少,他不能使用)constexpr
s。
它遵循了一个略有不同的解决方案,仍然基于之前的解决方案。
基本思想是使用 f
作为未评估的操作数 decltype
.
这是完整的代码:
#include<type_traits>
#include<cstddef>
static const std::size_t N = 42;
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<typename T, typename U>
struct types {
using basic = T;
using decorated = U;
};
template<typename T, typename U>
auto f(choice<0>) { return types<T, U>{}; }
template<typename T, typename U,
typename = std::enable_if_t<std::is_pointer<T>::value>>
auto f(choice<1>) {
auto t = f<std::remove_pointer_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_pointer_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_lvalue_reference<T>::value>>
auto f(choice<2>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_lvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
auto f(choice<3>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_rvalue_reference_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_array<T>::value>>
auto f(choice<4>) {
auto t = f<std::remove_extent_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::conditional_t<(0==std::extent<T>::value), D[], D[std::extent<T>::value]>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_const<T>::value>>
auto f(choice<5>) {
auto t = f<std::remove_const_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_const_t<D>>{};
}
template<typename T, typename U,
typename = std::enable_if_t<std::is_volatile<T>::value>>
auto f(choice<6>) {
auto t = f<std::remove_volatile_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_volatile_t<D>>{};
}
template<typename T, typename U>
auto f() {
return f<T, U>(choice<N>{});
}
template<typename T, typename U = char>
using my_type = decltype(f<T, U>());
template<typename T, typename U = char>
using my_type_basic_t = typename decltype(f<T, U>())::basic;
template<typename T, typename U = char>
using my_type_decorated_t = typename decltype(f<T, U>())::decorated;
int main() {
int i = 42;
my_type_decorated_t<char *, int> ptr = &i;
// of course, it can still be used in a constant expression if needed
// constexpr my_type_decorated_t<char *, int> ptr = nullptr;
}
因此,您可以使用模式匹配并执行转录的一个步骤的函数来执行此操作,如下所示:
template<class In, class Out>
struct types {
using type=types;
using in=In;
using out=Out;
};
// transcribe cv:
template<class In, class Out>
types<In, const Out> transcribe( types<const In, Out> ) { return {}; }
template<class In, class Out>
types<In, volatile Out> transcribe( types<volatile In, Out> ) { return {}; }
template<class In, class Out>
types<In, const volatile Out> transcribe( types<const volatile In, Out> ) { return {}; }
// references and pointers:
template<class In, class Out>
types<In, Out*> transcribe( types<In*, Out> ) { return {}; }
template<class In, class Out>
types<In, Out&> transcribe( types<In&, Out> ) { return {}; }
template<class In, class Out>
types<In, Out&&> transcribe( types<In&&, Out> ) { return {}; }
// arrays
template<class In, class Out>
types<In, Out[]> transcribe( types<In[], Out> ) { return {}; }
template<class In, class Out, std::size_t N>
types<In, Out[N]> transcribe( types<In[N], Out> ) { return {}; }
// return type of a function
template<class In, class...In_Args, class Out>
types<In, Out(In_Args...)> transcribe( types<In(In_Args...), Out> ) { return {}; }
// return type of a function
template<class In, class...In_Args, class Out>
types<In, Out(*)(In_Args...)> transcribe( types<In(*)(In_Args...), Out> ) { return {}; }
// default case
template<class X>
X transcribe( X ) { return {}; }
超载规则做正确的事。唯一令人讨厌的是 const volatile
案例。
一旦我们有了上面的内容,我们就可以把它变成一个特征:
template<class In, class Out>
struct transcribe_one:
decltype(transcribe( types<In,Out>{} ))
{};
很容易。请注意,永远不需要调用 transcribe
。
别名使它更易于使用:
template<class T>
using strip_one=typename transcribe_one<T, int>::in;
template<class T>
using can_strip=std::integral_constant<bool, !std::is_same<T, strip_one<T>>{}>;
template<class T, class U>
using typescribe_one=typename transcribe_one<T, U>::out;
也表示"is there anything to strip?".
这只会从左到右移动一种类型的装饰。要移动它们,我们只需这样做:
template<class In, class Out, class=void>
struct transcribe_all:types<In, Out> {};
template<class T>
using strip=typename transcribe_all<T, int>::in;
template<class T, class U>
using typescribe=typename transcribe_all<T, U>::out;
template<class In, class Out>
struct transcribe_all<In, Out, std::enable_if_t<
can_strip<In>{}
>> :
types<
strip< strip_one< In > >, // must strip on strip_one, trust me
typescribe_one<
In,
typescribe< strip_one<In>, Out >
>
>
{};
当你不能剥离时,它什么都不做。
当您可以剥离时,它会从 In 类型中剥离一个,将其余部分转录到 Out 上,然后在其结果上进行一步转录。
这给你两个别名:
template<class T>
using strip=// ...
template<class T, class U>
using typescribe=// ...
第一个采用类型 T
并将其剥离到底部的 "raw" 类型。
第二个采用类型 T
并将其所有装饰移动到 U
。
template<template<class...>class M, class U>
using under_map = typescribe< U, M<strip<U>> >;
剥离类型装饰,应用 M
,然后重新应用它们。
请注意,最多 "user friendly" 的工具是 strip<T>
和 typescribe<In, Out>
。第一个删除所有装饰器并获得 "underlying" 类型。第二个将装饰器从一种类型复制到另一种类型。使用 transcribe_all
或 transcribe_one
或 strip_one
等可能会导致混淆,它们是实现细节。
只需将类型映射写为 template
并将其传递给 under_map
。
唯一使用的 C++14 功能是 std::enable_if_t<?>
,在 C++11 上可以用 typename std::enable_if<?>::type
代替。
MSVC 可能对 decltype
的东西有问题,因为它对 SFINAE 中的 decltype 的支持很糟糕。
这是我的解决方案。它可以满足我的需要(无易失性)。
template <class T>
struct TypeIs {
using type = T;
};
template <class T>
struct GetPlainType : TypeIs<typename std::decay<T>::type> {};
template <class T>
struct GetPlainType<T const> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const &&> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T*> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const *> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
struct GetPlainType<T const[]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T[I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T, std::size_t I>
struct GetPlainType<T const [I]> : TypeIs<typename GetPlainType<T>::type> {};
template <class T>
using GetPlainType_t = typename GetPlainType<T>::type;
namespace detail {
//Qualifiers
struct ConstQual {};
//Category
struct ValueCat {};
template <std::size_t I = 0>
struct ArrayCat : std::integral_constant<std::size_t, I> {};
struct PointerCat {};
struct LValueReferenceCat {};
struct RValueReferenceCat {};
template <class Cat, class...Quals>
struct Decoration {
using Category = Cat;
using Qualifiers = std::tuple<Quals...>;
};
template <class Cat, class...Quals>
using DecorationCategory_t = typename Decoration<Cat, Quals...>::type;
template <class T>
struct SaveDecorations : TypeIs<brigand::list<Decoration<ValueCat>>> {};
template <class T>
struct SaveDecorations<T const> : TypeIs<brigand::list<Decoration<ValueCat, ConstQual>>> {};
template <class T>
struct SaveDecorations<T *> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<PointerCat>>>> {};
template <class T>
struct SaveDecorations<T * const> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<PointerCat, ConstQual>>>> {};
template <class T>
struct SaveDecorations<T &> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<LValueReferenceCat>>>> {};
template <class T>
struct SaveDecorations<T &&> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<RValueReferenceCat>>>> {};
template <class T>
struct SaveDecorations<T []> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<ArrayCat<>>>
>> {};
template <class T>
struct SaveDecorations<T const []> :
TypeIs<
brigand::append<
typename SaveDecorations<T const>::type,
brigand::list<Decoration<ArrayCat<>>>
>> {};
template <class T, std::size_t N>
struct SaveDecorations<T [N]> :
TypeIs<
brigand::append<
typename SaveDecorations<T>::type,
brigand::list<Decoration<ArrayCat<N>>>
>> {};
template <class T, std::size_t N>
struct SaveDecorations<T const [N]> :
TypeIs<
brigand::append<
typename SaveDecorations<T const>::type,
brigand::list<Decoration<ArrayCat<N>>>
>> {};
template <class State, class Elem>
struct AddDecoration : TypeIs<State> {};
template <class State>
struct AddDecoration<State, Decoration<ValueCat, ConstQual>> : TypeIs<State const> {};
template <class State>
struct AddDecoration<State, Decoration<PointerCat>> : TypeIs<State *> {};
template <class State>
struct AddDecoration<State, Decoration<PointerCat, ConstQual>> :
TypeIs<State * const> {};
template <class State>
struct AddDecoration<State, Decoration<LValueReferenceCat>> : TypeIs<State &> {};
template <class State>
struct AddDecoration<State, Decoration<RValueReferenceCat>> : TypeIs<State &&> {};
template <class State>
struct AddDecoration<State, Decoration<ArrayCat<>>> : TypeIs<State[]> {};
template <class State, std::size_t I>
struct AddDecoration<State, Decoration<ArrayCat<I>>> : TypeIs<State[I]> {};
template <class T, class DecorationsList>
struct ApplyDecorations :
TypeIs<brigand::fold<DecorationsList, T,
AddDecoration<brigand::_state,
brigand::_element>>> {};
}
template <class T>
using SaveDecorations_t = typename detail::SaveDecorations<T>::type;
template <class T, class TList>
using ApplyDecorations_t = typename detail::ApplyDecorations<T, TList>::type;
int main()
{
static_assert(std::is_same<GetPlainType_t<int>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int *>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int **>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int * &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int ** &>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const * []>, int>{}, "");
static_assert(std::is_same<GetPlainType_t<int const **[][3][5]>, int>{}, "");
using Decorations = SaveDecorations_t<int>;
using shouldBeFloat = ApplyDecorations_t<float, Decorations>;
static_assert(std::is_same<float, shouldBeFloat>{}, "");
using Decorations2 = SaveDecorations_t<int const>;
using shouldBeConst = ApplyDecorations_t<float, Decorations2>;
static_assert(std::is_same<shouldBeConst, float const>{}, "");
using Decorations3 = SaveDecorations_t<int const *>;
using shouldPointerToConst = ApplyDecorations_t<float, Decorations3>;
static_assert(std::is_same<shouldPointerToConst, float const *>{}, "");
using Decorations4 = SaveDecorations_t<int const * const>;
using shouldConstPointerToConst = ApplyDecorations_t<float, Decorations4>;
static_assert(std::is_same<shouldConstPointerToConst, float const * const>{}, "");
using Decorations5 = SaveDecorations_t<int const * const &>;
using shouldBeLValRefToConstPointerToConst = ApplyDecorations_t<float, Decorations5>;
static_assert(std::is_same<shouldBeLValRefToConstPointerToConst, float const * const &>{}, "");
using Decorations6 = SaveDecorations_t<int * const ** const &>;
using Res = ApplyDecorations_t<float, Decorations6>;
static_assert(std::is_same<Res, float * const ** const &>{}, "");
using Decorations7 = SaveDecorations_t<int * const>;
using Res2 = ApplyDecorations_t<float, Decorations7>;
static_assert(std::is_same<Res2, float * const>{}, "");
//Arrays tests
using Decorations8 = SaveDecorations_t<int const * const * const []>;
using Res3 = ApplyDecorations_t<float, Decorations8>;
static_assert(std::is_same<Res3, float const * const * const []>{}, "");
using Decorations9 = SaveDecorations_t<int const * const * [3]>;
using Res4 = ApplyDecorations_t<float, Decorations9>;
static_assert(std::is_same<Res4, float const * const * [3]>{}, "");
//Multidimensional arrays
using Decorations10 = SaveDecorations_t<int const * const * [3][5]>;
using Res5 = ApplyDecorations_t<float, Decorations10>;
static_assert(std::is_same<Res5, float const * const * [3][5]>{}, "");
using Decorations11 = SaveDecorations_t<int const * const * [][3][5]>;
using Res6 = ApplyDecorations_t<float, Decorations11>;
static_assert(std::is_same<Res6, float const * const * [][3][5]>{}, "");
}