Способ определить, принадлежит ли тип шаблонному классу

#c #templates #types

#c #шаблоны #типы

Вопрос:

Я пытался ответить на вопрос в названии, но я в тупике. В основном, пытаюсь выяснить, есть ли встроенный способ указать «источник» экземпляра шаблона, по крайней мере, для классов. Вот пример того, что я хотел бы сделать:

 template<class T>
class A { };

auto a = A<int>();

template<class T>
auto someFunction(T item) {
    if(/* if type of a is from the templated class A */) {
        // yep A<int> is 'from' A.
    }
}
  

Возможно ли это каким-либо способом, подобным этому? Я мог бы использовать некоторые сохраненные значения или махинации с наследованием, чтобы получить что-то подобное, но я бы предпочел этого не делать.

Комментарии:

1. просто A<T> , или любой C<Ts...> , или также std::array<T, N> ?

2. Вы хотите определить struct Der : A<int> {} ?

3. @Jarod42 просто любые A<T> s, но ничего больше. Итак, исключая ваши следующие два примера.

Ответ №1:

Возможно, с помощью пользовательских свойств типа.

Что-то вроде следующего

 template <typename>
struct is_A : public std::false_type
 { };

template <typename T>
struct is_A<A<T>> : public std::true_type
 { };

// ...

template <typename T>
auto someFunction(T item) {
   if( is_A<T>::value ) {
    // yep A<int> is 'from' A.
   }
}
  

Или, может быть, вы хотите перехватывать не только A<T> , но, в общем, все типы, являющиеся шаблонным типом, возможно, с неопределенным количеством аргументов шаблона?

В этом случае вы можете попробовать что-то вроде следующего

 template <typename>
struct is_template : public std::false_type
 { };

template <template <typename...> class C, typename ... Ts>
struct is_template<C<Ts...>> : public std::true_type
 { };
  

Проблема: этот тип traits перехватывает все типы шаблонов с аргументами шаблона types и только аргументы шаблона. Но не перехватывает, например, std::integer_sequence<int, 0, 1, 2, 3, 4, 5> которые получают тип и некоторый параметр шаблона, не относящийся к типу.

Вы можете добавить другие специализации для is_template , чтобы перехватить другие случаи, но вы не можете определить специализацию, которая улавливает все типы шаблонов (все комбинации параметров шаблона).

Комментарии:

1. Обратите внимание, что это нарушает LSP; сделайте A окончательным, если вы это сделаете.

2. @Yakk-AdamNevraumont — эммм … извините, я не понимаю, что вы имеете в виду.

3. Ах. en.wikipedia.org/wiki/Liskov_substitution_principle — что производный тип может быть заменен на его родительский. Если A<int> не является окончательным, а B является производным от A<int> , ваш код при передаче будет вести себя иначе, A<int> чем B . Следовательно, нарушает LSP.

4. @max66 Я считаю, что это нарушение подстановки LSP можно было бы устранить, если бы шаблон использовался std::condition для определения, унаследован ли он от true_type или false_type , вместо использования специализации.

Ответ №2:

Используйте признак типа

 #include <type_traits>
#include <iostream>

template<class T>
class A { };

auto a = A<int>();

template <typename X>
struct is_from_A : std::false_type {};

template <typename T>
struct is_from_A<A<T>> : std::true_type {};

int main() {
    std::cout << is_from_A<int>::value << "n";     // 0
    std::cout << is_from_A<A<int>>::value << "n";  // 1
}
  

Комментарии:

1. Прекрасно! Спасибо. Как называется синтаксис в строке struct is_from_A<A<T>> : ... ? Я думал, что специализации шаблонов могут использовать только явные значения, т.Е. is_from_A<A<int>> : ... , но, по-видимому, это не так.

2. @AnthonyMonterrosa: И поэтому это частичная специализация (кстати, недоступная для функции).

Ответ №3:

Другой способ, позволяющий производному классу соответствовать:

 template <typename T>
std::true_type is_an_A_impl(A<T>*);

std::false_type is_an_A_impl(...);

template <typename T>
using is_a_A = decltype(is_an_A_impl(std::declval<T*>()));