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

У меня есть функция, которая принимает тип шаблона для определения возвращаемого значения. Есть ли способ сказать во время компиляции, является ли тип шаблона некоторым экземпляром класса шаблона?

Ex.

class First { /* ... */ };

template <typename T>
class Second { /* ... */ };

using MyType = boost::variant<First, Second<int>, Second<float>>;

template <typename SecondType>
auto func() -> MyType {
    static_assert(/* what goes here?? */, "func() expects Second type");
    SecondType obj;
    // ...
    return obj;
}

MyType obj = func<Second<int>>();

Я знаю, что можно обойти это, делая

template <typename T>
auto func() -> MyType {
    static_assert(std::is_same<T, int>::value || std::is_same<T, float>::value,
                  "func template must be type int or float");

    Second<T> obj;
    // ...
    return obj;
}

MyType obj = func<int>();

Мне просто любопытно в общем, если есть способ проверить, является ли тип экземпляром класса шаблона? Потому что, если MyType заканчивается 6 Second экземпляры, я не хочу проверять все возможные типы.

4 ответа

Решение

Вот вариант:

#include <iostream>
#include <type_traits>
#include <string>

template <class, template <class> class>
struct is_instance : public std::false_type {};

template <class T, template <class> class U>
struct is_instance<U<T>, U> : public std::true_type {};

template <class>
class Second 
{};

int main()
{
    using A = Second<int>;
    using B = Second<std::string>;
    using C = float;
    std::cout << is_instance<A, Second>{} << '\n'; // prints 1
    std::cout << is_instance<B, Second>{} << '\n'; // prints 1
    std::cout << is_instance<C, Second>{} << '\n'; // prints 0
}

Это в основном специализируется на is_instance структура для типов, которые являются экземплярами шаблона.

Другой вариант, взяв комментарий Анри:

#include <iostream>
#include <type_traits>
#include <string>

template <class, template <class, class...> class>
struct is_instance : public std::false_type {};

template <class...Ts, template <class, class...> class U>
struct is_instance<U<Ts...>, U> : public std::true_type {};

template <class>
class Second 
{};

template <class, class, class>
class Third 
{};

int main()
{
    using A = Second<int>;
    using B = Second<std::string>;
    using C = float;
    using D = Third<std::string, int, void>;
    std::cout << is_instance<A, Second>{} << '\n'; // prints 1
    std::cout << is_instance<B, Second>{} << '\n'; // prints 1
    std::cout << is_instance<C, Second>{} << '\n'; // prints 0
    std::cout << is_instance<D, Third>{} << '\n'; // prints 1
}

Еще одно улучшение ответа @RichardHodges: обычно также требуется захватить не только простые типы, но и все типы с квалификацией cv и типы с квалификацией ref.

Другими словами, если is_instance<A, Second>{} верно, также is_instance<A const&, Second>{} или is_instance<A&&, Second>{}должно быть правдой. Текущие реализации в этом потоке не поддерживают это.

Следующий код учитывает все типы cv-ref, добавляя еще одно косвенное обращение и std::decay_t:

namespace
{
    template <typename, template <typename...> typename>
    struct is_instance_impl : public std::false_type{};

    template <template <typename...> typename U, typename...Ts>
    struct is_instance_impl<U<Ts...>, U> : public std::true_type{};
}

template <typename T, template <typename ...> typename U>
using is_instance = is_instance_impl<std::decay_t<T>, U>;

Используйте это как

#include <iostream>

template <typename ...> struct foo{};
template <typename ...> struct bar{};

int main()
{
    std::cout << is_instance<foo<int>, foo>{} << std::endl;           // prints 1
    std::cout << is_instance<foo<float> const&, foo>{} <<std::endl;   // prints 1
    std::cout << is_instance<foo<double,int> &&, foo>{} << std::endl; // prints 1
    std::cout << is_instance<bar<int> &&, foo>{} << std::endl;        // prints 0
}

Это работает для int аргументы шаблона:

      template <class, template <int, int...> class>
struct is_instance : public std::false_type {};

template <int...Ts, template <int, int...> class U>
struct is_instance<U<Ts...>, U> : public std::true_type {};
Другие вопросы по тегам