使用 SFINAE 检查类型是否可以绑定到模板模板参数
Use SFINAE to check if types can be bound to template template parameter
是否可以测试某些类型是否可以通过 SFINAE 绑定到模板模板参数?
我认为我尝试做的最好用下面的示例代码来解释:
#include <iostream>
template<typename... T> using void_t = void;
template<typename T> struct TemporaryBindObject
{
using type = TemporaryBindObject<T>;
};
template<template<typename...> class Dest> struct TestValidBind
{
template<typename... Ts> struct toTypesOf
{
using type = std::false_type;
};
template<template<typename...> class Src, typename... Ts> struct toTypesOf<Src<Ts...>, void_t<Dest<Ts...,float>>>
{
using type = std::true_type;
};
};
template<typename T> struct OneParamStruct{};
template<typename T1, typename T2> struct TwoParamStruct{};
int main()
{
using tmp = TemporaryBindObject<int>;
std::cout << "Can bind to TwoParamStruct: " << TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type::value << std::endl;
std::cout << "Can bind to OneParamStruct: " << TestValidBind<OneParamStruct>::toTypesOf<tmp>::type::value << std::endl;
}
首先,我创建了一个临时类型 tmp
,我想从中获取模板参数 int
以将其绑定到另一个 class 模板。
使用 TestValidBind<template template type>::toTypesOf<typename>
我想测试是否可以将给定类型的参数绑定到 template template parameter
和 并附加一个附加类型(float
在示例中)。
我想要的是 TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type
是 true_type
而 TestValidBind<OneParamStruct>::toTypesOf<tmp>::type
是 false_type
.
代码示例不使用 g++ -std=c++11
(5.3.1) 编译,出现以下错误:
../test_SFINAE_with_template_binding.cc: In function ‘int main()’:
../test_SFINAE_with_template_binding.cc:34:96: error:
‘TestValidBind<OneParamStruct>::toTypesOf<TemporaryBindObject<int>
>::type’ has not been declared
如果 OneParamStruct
行被删除, 并报告 false_type
(这是错误的)。
使用 clang++ -std=c++11
(3.8.0) 代码编译但在两种情况下都报告 false_type
。
有可能吗?
编辑: 将附加类型从 void
更改为 float
以突出显示我想检查附加类型是否可行。
void_t
技巧要求我们为主模板的参数之一提供默认类型 void
。您不需要主模板 (toTypesOf
) 是可变的。
bool
类型特征继承自 false_type
或 true_type
而不是嵌套 type
更为惯用。 TemporaryBindObject
不需要嵌套 type
.
TestValidBind
应该看起来像这样:
template<template<typename...> class Dest> struct TestValidBind
{
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type
{};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>>
: std::true_type
{};
};
编辑: 如您所说,这不适用于 g++,我不知道为什么会这样。但我们可以简化它,看看是否有帮助。我们实际上并不需要封闭结构 TestValidBind
。如果我们将 Dest
参数移植到其中,toTypesOf
模板可以在命名空间范围内:
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type
{};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>>
: std::true_type
{};
这适用于 g++。
如果您愿意,我们可以更进一步,将所有内容放入 detail
命名空间并将其包装在别名模板中:
namespace detail
{
template<typename... T> using void_t = void;
template<typename... T> struct TemporaryBindObject {};
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type {};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts...>>>
: std::true_type {};
}
template<template<typename...> class Dest, typename... Ts>
using IsValidBind = typename detail::toTypesOf<Dest, detail::TemporaryBindObject<Ts...>>;
template<template<typename...> class Dest, typename... Ts>
using IsValidBindWithFloat = IsValidBind<Dest, Ts..., float>;
template<template<typename...> class Dest, typename... Ts>
using IsValidBindWithVoid = IsValidBind<Dest, Ts..., void>;
std::cout << "Can bind to TwoParamStruct: " << IsValidBindWithFloat<TwoParamStruct, int>::value << std::endl;
std::cout << "Can bind to OneParamStruct: " << IsValidBindWithFloat<OneParamStruct, int>::value << std::endl;
现在我们不需要 using tmp
,我们有一个更通用的解决方案,您可以在其中轻松更改要用作附加类型的类型。
这是我的做法:
using std::void_t; // or write your own
template<class T>struct tag{using type=T;};
template<template<class...>class Z>struct ztag{
template<class...Ts>using result=Z<Ts...>;
};
namespace details {
template<class Src, class Target, class=void>
struct rebind {};
template<template<class...>class Src, template<class...>class Target, class...Ts>
struct rebind<Src<Ts...>, ztag<Target>, void_t<Target<Ts...>>>:
tag<Target<Ts...>>
{};
}
template<class Src, class zDest>
using rebind = typename details::rebind<Src,zDest>::type;
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;
template<class...>struct types{using type=types;};
namespace details {
template<class types, class...Us>
struct append;
template<class...Ts, class...Us>
struct append<types<Ts...>, Us...>:
types<Ts..., Us...>
{};
}
template<class types, class...Us>
using append = typename details::append<types, Us...>::type;
template<class Src, template<class...>class Dest>
using can_rebind_with_void =
can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >;
ztag
是因为元编程在仅使用类型时要容易得多。 ztag
采用模板并将其转换为类型。
zapply
然后应用它:
namespace details {
template<class Z, class...Ts>
struct zapply {};
template<template<class...>class Z, class...Ts>
struct zapply<ztag<Z>, Ts...>:
tag<Z<Ts...>>
{};
}
template<class Z, class...Ts>
using zapply = typename details::zapply<Z,Ts...>::type;
无论如何,除了单行解决方案之外,一切都是通用的:
template<class Src, template<class...>class Dest>
using can_rebind_with_void =
can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >;
这里我们问"can we apply"rebind< ???, ztag<Dest> >
。这是 Dest<???>
.
???
以 Src<???>
开头,将其类型移至 types<???>
,并附加一个 void
。所以我们从 Src<Ts...>
.
得到 types<Ts..., void>
rebind
采用带有模板参数的类型和另一个模板的 ztag
,并将第一个参数模板参数应用于 ztag
.[=32= 中的模板]
can_apply
询问隐含模板应用是否合法。如果我更一致,can_apply
也会将 ztag
作为其第一个参数。
我用 ztag
做这一切的原因是因为模板元编程更流畅,因为一切都是类型。
是否可以测试某些类型是否可以通过 SFINAE 绑定到模板模板参数?
我认为我尝试做的最好用下面的示例代码来解释:
#include <iostream>
template<typename... T> using void_t = void;
template<typename T> struct TemporaryBindObject
{
using type = TemporaryBindObject<T>;
};
template<template<typename...> class Dest> struct TestValidBind
{
template<typename... Ts> struct toTypesOf
{
using type = std::false_type;
};
template<template<typename...> class Src, typename... Ts> struct toTypesOf<Src<Ts...>, void_t<Dest<Ts...,float>>>
{
using type = std::true_type;
};
};
template<typename T> struct OneParamStruct{};
template<typename T1, typename T2> struct TwoParamStruct{};
int main()
{
using tmp = TemporaryBindObject<int>;
std::cout << "Can bind to TwoParamStruct: " << TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type::value << std::endl;
std::cout << "Can bind to OneParamStruct: " << TestValidBind<OneParamStruct>::toTypesOf<tmp>::type::value << std::endl;
}
首先,我创建了一个临时类型 tmp
,我想从中获取模板参数 int
以将其绑定到另一个 class 模板。
使用 TestValidBind<template template type>::toTypesOf<typename>
我想测试是否可以将给定类型的参数绑定到 template template parameter
和 并附加一个附加类型(float
在示例中)。
我想要的是 TestValidBind<TwoParamStruct>::toTypesOf<tmp>::type
是 true_type
而 TestValidBind<OneParamStruct>::toTypesOf<tmp>::type
是 false_type
.
代码示例不使用 g++ -std=c++11
(5.3.1) 编译,出现以下错误:
如果../test_SFINAE_with_template_binding.cc: In function ‘int main()’: ../test_SFINAE_with_template_binding.cc:34:96: error: ‘TestValidBind<OneParamStruct>::toTypesOf<TemporaryBindObject<int> >::type’ has not been declared
OneParamStruct
行被删除, 并报告 false_type
(这是错误的)。
使用 clang++ -std=c++11
(3.8.0) 代码编译但在两种情况下都报告 false_type
。
有可能吗?
编辑: 将附加类型从 void
更改为 float
以突出显示我想检查附加类型是否可行。
void_t
技巧要求我们为主模板的参数之一提供默认类型 void
。您不需要主模板 (toTypesOf
) 是可变的。
bool
类型特征继承自 false_type
或 true_type
而不是嵌套 type
更为惯用。 TemporaryBindObject
不需要嵌套 type
.
TestValidBind
应该看起来像这样:
template<template<typename...> class Dest> struct TestValidBind
{
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type
{};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>>
: std::true_type
{};
};
编辑: 如您所说,这不适用于 g++,我不知道为什么会这样。但我们可以简化它,看看是否有帮助。我们实际上并不需要封闭结构 TestValidBind
。如果我们将 Dest
参数移植到其中,toTypesOf
模板可以在命名空间范围内:
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type
{};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts..., float>>>
: std::true_type
{};
这适用于 g++。
如果您愿意,我们可以更进一步,将所有内容放入 detail
命名空间并将其包装在别名模板中:
namespace detail
{
template<typename... T> using void_t = void;
template<typename... T> struct TemporaryBindObject {};
template<template<typename...> class Dest, typename T, typename = void_t<>> struct toTypesOf
: std::false_type {};
template<template<typename...> class Dest, template<typename...> class Src, typename... Ts> struct toTypesOf<Dest, Src<Ts...>, void_t<Dest<Ts...>>>
: std::true_type {};
}
template<template<typename...> class Dest, typename... Ts>
using IsValidBind = typename detail::toTypesOf<Dest, detail::TemporaryBindObject<Ts...>>;
template<template<typename...> class Dest, typename... Ts>
using IsValidBindWithFloat = IsValidBind<Dest, Ts..., float>;
template<template<typename...> class Dest, typename... Ts>
using IsValidBindWithVoid = IsValidBind<Dest, Ts..., void>;
std::cout << "Can bind to TwoParamStruct: " << IsValidBindWithFloat<TwoParamStruct, int>::value << std::endl;
std::cout << "Can bind to OneParamStruct: " << IsValidBindWithFloat<OneParamStruct, int>::value << std::endl;
现在我们不需要 using tmp
,我们有一个更通用的解决方案,您可以在其中轻松更改要用作附加类型的类型。
这是我的做法:
using std::void_t; // or write your own
template<class T>struct tag{using type=T;};
template<template<class...>class Z>struct ztag{
template<class...Ts>using result=Z<Ts...>;
};
namespace details {
template<class Src, class Target, class=void>
struct rebind {};
template<template<class...>class Src, template<class...>class Target, class...Ts>
struct rebind<Src<Ts...>, ztag<Target>, void_t<Target<Ts...>>>:
tag<Target<Ts...>>
{};
}
template<class Src, class zDest>
using rebind = typename details::rebind<Src,zDest>::type;
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;
template<class...>struct types{using type=types;};
namespace details {
template<class types, class...Us>
struct append;
template<class...Ts, class...Us>
struct append<types<Ts...>, Us...>:
types<Ts..., Us...>
{};
}
template<class types, class...Us>
using append = typename details::append<types, Us...>::type;
template<class Src, template<class...>class Dest>
using can_rebind_with_void =
can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >;
ztag
是因为元编程在仅使用类型时要容易得多。 ztag
采用模板并将其转换为类型。
zapply
然后应用它:
namespace details {
template<class Z, class...Ts>
struct zapply {};
template<template<class...>class Z, class...Ts>
struct zapply<ztag<Z>, Ts...>:
tag<Z<Ts...>>
{};
}
template<class Z, class...Ts>
using zapply = typename details::zapply<Z,Ts...>::type;
无论如何,除了单行解决方案之外,一切都是通用的:
template<class Src, template<class...>class Dest>
using can_rebind_with_void =
can_apply< rebind, append< rebind<Src, ztag<types>>, void >, ztag<Dest> >;
这里我们问"can we apply"rebind< ???, ztag<Dest> >
。这是 Dest<???>
.
???
以 Src<???>
开头,将其类型移至 types<???>
,并附加一个 void
。所以我们从 Src<Ts...>
.
types<Ts..., void>
rebind
采用带有模板参数的类型和另一个模板的 ztag
,并将第一个参数模板参数应用于 ztag
.[=32= 中的模板]
can_apply
询问隐含模板应用是否合法。如果我更一致,can_apply
也会将 ztag
作为其第一个参数。
我用 ztag
做这一切的原因是因为模板元编程更流畅,因为一切都是类型。