使用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
,将其绑定到另一个类模板。使用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:错误:'测试有效绑定<OneParamStruct>::到类型Of<TemporaryBindObject<int>>:类型"尚未声明为
并且如果CCD_ 12线被移除则报告CCD_。
对于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
,我们有了一个更通用的解决方案,您可以轻松地更改要用作附加类型的类型。
DEMO
以下是我的操作方法:
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> >;
在这里,我们询问"我们可以申请"rebind< ???, ztag<Dest> >
。这将执行Dest<???>
。
???
从Src<???>
开始,将其类型移到types<???>
,并附加一个void
。所以我们从Src<Ts...>
得到types<Ts..., void>
。
rebind
采用一个带有模板args的类型,以及另一个模板的ztag
,并将第一个参数模板args应用于ztag
内的模板。
can_apply
询问隐含模板应用程序是否合法。如果我更一致的话,can_apply
也会把ztag
作为它的第一个自变量。
我用ztag
做这一切的原因是因为模板元编程更流畅,因为一切都是类型。
- 为什么 std::绑定错误参数可以成功?
- C++初始化 std::function 时如何将占位符绑定到引用/引用参数?
- 绑定到可变参数成员函数
- 如何在 C# 中将带有参数的函数绑定到包中
- 如何在宏中正确转发结构化绑定参数
- std::绑定可变参数模板、绑定参数和占位符
- libpqxx:如何绑定参数数组
- 传递 boost::绑定参数作为参数
- std::绑定参数复制行为
- 绑定参数多于所需的函数,并向其传递明确的参数
- 信号和绑定参数
- 使用尽可能少的代码将非静态方法包装到 std::函数中"this"并绑定参数
- C++sqlite3绑定参数
- c++使用ODBC绑定参数预先确定VARCHAR的长度
- 在SQLite3中绑定参数是如何工作的(最小示例)
- 如何在Postgresql中准备语句和绑定参数
- 如何调试SQLite3中的绑定参数
- 在设置函数指针之前绑定参数
- 使用boost::fusion::for_each时传递/绑定参数
- 使用std::bind分别绑定参数和对象实例