如何比较模板模板和模板实例?

时间:2015-03-04 12:08:35

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

首先,让我向您介绍一个部分解决方案:

template <template <class...> class,
        typename ...>
struct is_tbase_of:
  std::false_type
{ };

template <template <class...> class Type,
          typename ...Args>
struct is_tbase_of<Type, Type<Args...>>:
  std::true_type
{ };

在一般情况下,它有效:

is_tbase_of<std::vector, std::is_integral<int>>::value; // false
is_tbase_of<std::vector, std::vector<int>>::value;      // true

但是,它不适用于“元返回”模板模板,例如:

template <template <class ...> class T>
struct quote
{
  template <typename ...U>
  using type = T<U...>;
};

using QVec =  quote<std::vector>;
is_tbase_of<QVec::template type, std::vector<int>>::value; // false...

我尝试了很多东西,尝试获取第二个类型模板参数(比较引用的类型特化)但似乎我无法让它们工作。即使专门为is_tbase_of设置quote(这将是一个不那么通用但足够的选项),似乎会将我发送到模板模式匹配的黑角。

4 个答案:

答案 0 :(得分:4)

您可以检查是否可以将U<Args...>更改为T<Args...>,然后检查结果是否保持不变:

#include <type_traits>
#include <vector>

struct is_tbase_of_impl
{
    struct err {};

    template <template <class...> class T, class U>
    static err test(U*);

    template <template <class...> class T, template <class...> class U, class... Args>
    static T<Args...> test(U<Args...>*);
};

template <template <class...> class T, class U>
using is_tbase_of
    = typename std::is_same< decltype(is_tbase_of_impl::test<T>((U*)0)), U >::type;

template <template <class...> class T>
struct quote
{
    template <class... U>
    using type = T<U...>;
};

using QVec = quote<std::vector>;

template <class...> struct S {};

static_assert( !is_tbase_of< std::vector, std::is_integral<int>  >::value, "" );
static_assert(  is_tbase_of< std::vector, std::vector<int>       >::value, "" );
static_assert(  is_tbase_of< QVec::type,  std::vector<int>       >::value, "" );
static_assert( !is_tbase_of< std::vector, S<int, int, int>       >::value, "" );

int main()
{
}

答案 1 :(得分:4)

这是尝试用直接模板结构元编程和SFINAE解决问题。

该计划是2倍。首先,一个traits类,它接受一个模板和一组参数,以及答案&#34;将一组参数应用于模板&#34;是合法的。这是一个非常有用的结构:例如,给定SFINAE友好result_of_t<F(Args...)>,您可以在一行中写can_invoke<F(Args...)>

其次,我们写is_template_instance_of。此处的目标是采用T<Args...>类型和Z<?...>模板,并查看Z<Args...>T<Args...>的类型是否相同。我们使用上面的can_apply traits类来防止非法替换,然后进行简单的is_same测试。

该解决方案会产生一些误报和否定,具体取决于您如何看待它。基本上,如果我们匹配的模板Z<?...>是一个不是直接别名的别名模板,它就会以您期望的方式工作。如果它是一个直接的别名,那么你就会很好。

不用多说,这是实现。

首先,Boilerplate类型包:

template<class...>struct types {using type=types;};

在C ++中,1z有void_t,我在这里重新实现了它:

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;

给定Z<?...>types<Ts...>,检查Z<Ts...>是否有效:

template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, types<Ts...>, void_t<Z<Ts...>>> : std::true_type {};

现在,SFINAE守卫测试:

template<template<class...>class Z, class T, class=void>
struct is_template_instance_of : std::false_type {};

template<template<class...>class Z, template<class...>class Y, class... Ts>
struct is_template_instance_of<
  Z, Y<Ts...>,
  std::enable_if_t<can_apply< Z, types<Ts...> >{}>
> : std::is_same< Z<Ts...>, Y<Ts...> > {};

live example

答案 2 :(得分:3)

原始实施不起作用的原因是,即使QVec::type<Args...>std:vector<Args...>的类型相同,QVec::type也不是同一个模板< / strong>为std::vector,因此它与部分专业化不匹配。

这可以通过一个更简单的例子看出:

template <template <typename...> class> struct test {
  static const bool value = false;
};

template <>
struct test<std::vector> {
  static const bool value = true;
};

test<std::vector>::value; // true
test<QVec::type>::value;  // false

这是一种几乎可行的方法:

template <template <class...> class Type1,
          template <class...> class Type2,
          typename... Args>
struct is_tbase_of<Type1, Type2<Args...>>:
  std::is_same<Type1<Args...>,Type2<Args...>>
{
};

但是,正如@Alex所指出的,这并不能处理第二个模板的参数与第一个模板不兼容的情况。这可以使用enable_if

来解决
template <template <class...> class, typename, typename=void>
struct is_tbase_of : std::false_type { };

template <template <class...> class Type1,
          template <class...> class Type2,
          typename... Args>
struct is_tbase_of<Type1, Type2<Args...>,
  typename std::enable_if<
    std::is_same<Type1<Args...>,Type2<Args...>>::value
  >::type>
  : std::true_type
{
};

答案 3 :(得分:2)

如果is_tbase_of专门用于quote就足够了,这应该可行:

template <template <class...> class Type,
          typename ...Args>
struct is_tbase_of<quote<Type>::template type, Type<Args...>>:
  std::true_type
{ };