问题 如何将模板模板与模板实例进行比较?


首先,我来介绍一个部分解决方案:

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 (这将是一个不太通用但足够的选项)似乎把我送到模板模式匹配的黑角。


6606
2018-03-04 12:08


起源



答案:


您可以检查是否可以更改 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()
{
}

4
2018-03-04 13:49



聪明。然而, 有一个小洞。非常小,不值得疑惑,但看看最后一个断言。有趣的是如果争论的话 S 与参数相同 S2_, static_assert 失败! - Yakk - Adam Nevraumont
非常好:-)不那么令人惊讶,但是这个 template <class...> using S2 = void; 永远都会失败...... - Alex
但是,我认为这不会产生误报。这是一个问题:你的解决方案是否会产生不同的答案 矿?如果是这样,谁更正确? (我实际上认为答案是“不”) - Yakk - Adam Nevraumont
我以为你的意思是 is_tbase_of<S2,S2<int,void>  好像 它应该返回true(与你的 S2),但这有效地呼吁 is_tbase_of<S2,S<int>> 同 template <class...> using V = void 它永远是 is_tbase_of<V,V</*args...*/>> = false 虽然它 好像 它应该总是返回true。 - Alex
点头。误报就是这种情况 不 返回 true,但它应该返回false。这俩 S2 和 V case返回false,即使它看起来应该返回true。如果我更换 type = S<int> 同 type = S<int,void> 在里面 S2 情况下,返回值确实变为 true,但这不是一个“虚假”的积极因素 S2<int,void> 确实生产 S2<int,void>。我猜 is_tbase_of<S2, S<int,void>> 是假阳性(有 type=S<int,void>)从某种意义上说? - Yakk - Adam Nevraumont


答案:


您可以检查是否可以更改 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()
{
}

4
2018-03-04 13:49



聪明。然而, 有一个小洞。非常小,不值得疑惑,但看看最后一个断言。有趣的是如果争论的话 S 与参数相同 S2_, static_assert 失败! - Yakk - Adam Nevraumont
非常好:-)不那么令人惊讶,但是这个 template <class...> using S2 = void; 永远都会失败...... - Alex
但是,我认为这不会产生误报。这是一个问题:你的解决方案是否会产生不同的答案 矿?如果是这样,谁更正确? (我实际上认为答案是“不”) - Yakk - Adam Nevraumont
我以为你的意思是 is_tbase_of<S2,S2<int,void>  好像 它应该返回true(与你的 S2),但这有效地呼吁 is_tbase_of<S2,S<int>> 同 template <class...> using V = void 它永远是 is_tbase_of<V,V</*args...*/>> = false 虽然它 好像 它应该总是返回true。 - Alex
点头。误报就是这种情况 不 返回 true,但它应该返回false。这俩 S2 和 V case返回false,即使它看起来应该返回true。如果我更换 type = S<int> 同 type = S<int,void> 在里面 S2 情况下,返回值确实变为 true,但这不是一个“虚假”的积极因素 S2<int,void> 确实生产 S2<int,void>。我猜 is_tbase_of<S2, S<int,void>> 是假阳性(有 type=S<int,void>)从某种意义上说? - Yakk - Adam Nevraumont


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

该计划是2倍。首先,一个traits类,它接受一个模板和一个参数包,并回答“将一组参数应用于模板是合法的”。这是一个令人惊讶的有用结构:作为一个例子,给予SFINAE友好 result_of_t<F(Args...)>, 你可以写 can_invoke<F(Args...)> 在一行。

其次,我们写 is_template_instance_of。这里的目标是采取一个 T<Args...> 类型和 Z<?...> 模板,看看是否 Z<Args...> 与...类型相同 T<Args...>。我们使用上面的 can_apply 特质类防止非法替代,然后做一个简单的 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...> > {};

实例


4
2018-03-04 21:20





你的原始实现不起作用的原因是,尽管如此 QVec::type<Args...> 与...类型相同 std:vector<Args...>QVec::type 不一样 模板 如 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
2018-03-04 14:04



好的,但这只有在有效的情 Type1<Args...> 已验证。否则,这会导致编译器错误。 - Alex
如果只有SFINAE也用于部分模板专业化选择 - Joe
@Joe如何对部分模板专业化无效?上面的问题是之后的部分 : 不适用于发生替代的SFINAE。 - Yakk - Adam Nevraumont
@Yakk结构体内没有SFINAE可以发生的地方,这会妨碍专业化的使用,对吧? - Joe
@joe不是我所知道的。只是替换的直接上下文,在标准中定义。 - Yakk - Adam Nevraumont


如果专业 is_tbase_of 对于 quote 这是足够的,这应该工作:

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

2
2018-03-04 12:40



演示 - TartanLlama