使用SFINAE检查类型是否可以绑定到模板模板参数

时间:2016-04-04 19:46:49

标签: c++ templates c++11 metaprogramming

是否可以测试某些类型是否可以通过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>::typetrue_typeTestValidBind<OneParamStruct>::toTypesOf<tmp>::typefalse_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,以突出显示我想检查其他类型是否可行。

2 个答案:

答案 0 :(得分:2)

void_t技巧要求我们将主模板的其中一个参数设为默认类型void。您不需要主模板(toTypesOf)是可变参数。

bool类型特征从false_typetrue_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 ++。

DEMO

我们可以更进一步,如果您愿意,可以通过将所有内容放在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

答案 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> >;

Live example

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做这一切的原因是因为模板元编程更顺畅,一切都是类型。