是否可以测试某些类型是否可以通过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
以将其绑定到另一个类模板。
使用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:在函数'int main()'中: ../test_SFINAE_with_template_binding.cc:34:96:错误: “TestValidBind&LT; OneParamStruct&GT; :: toTypesOf&LT; TemporaryBindObject&LT; INT&GT; &gt; :: type'尚未声明
false_type
行,则并报告OneParamStruct
(这是错误的)。
使用clang++ -std=c++11
(3.8.0)代码会编译但在两种情况下都会报告false_type
。
这样的事情是否可能?
修改:将其他类型从void
更改为float
,以突出显示我想检查其他类型是否可行。
答案 0 :(得分:2)
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
。如果我们将toTypesOf
参数移植到其中,Dest
模板可以在命名空间范围内:
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
,我们有一个更通用的解决方案,您可以在其中轻松更改要用作其他类型的类型。
答案 1 :(得分:1)
以下是我的表现:
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> >;
我们在此问及&#34;我们可以申请&#34; rebind< ???, ztag<Dest> >
。这样做Dest<???>
。
???
以Src<???>
开头,将其类型移至types<???>
,附加void
。我们从types<Ts..., void>
获得Src<Ts...>
。
rebind
采用带有模板参数的类型和另一个模板的ztag
,并将第一个参数模板args应用于ztag
内的模板。
can_apply
询问隐含的模板应用程序是否合法。如果我更加一致,can_apply
也会将ztag
作为第一个参数。
我使用ztag
做这一切的原因是因为模板元编程更顺畅,一切都是类型。