Как сравнить шаблон шаблона с экземпляром шаблона?

Во-первых, позвольте мне представить вам частичное решение:

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

Но это не работает на шаблоне шаблона "meta-return", например:

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 (который был бы менее общим, но достаточным вариантом), по-видимому, отправляет меня в черные углы сопоставления шаблонов шаблонов.

Ответ 1

Вы можете проверить, можете ли вы изменить 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()
{
}

Ответ 2

Это попытка решить проблему с помощью метапрограммирования прямой структуры шаблона и SFINAE.

План 2 раза. Во-первых, класс признаков, который принимает шаблон и пакет аргументов, и ответы "законно применять пакет аргументов к шаблону". Это удивительно полезная структура: в качестве примера, с учетом дружественного 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 pack:

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

В С++ 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...> > {};

живой пример

Ответ 3

Причина, по которой ваша исходная реализация не работает, заключается в том, что, хотя 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
{
};

Ответ 4

Если специализация is_tbase_of для quote достаточна, это должно работать:

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