Проверьте, какие базовые классы реализованы - PullRequest
0 голосов
/ 29 апреля 2018

У меня есть два шаблона классов A и B

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

Теперь я хотел бы написать функцию, которая выполняет различные действия в зависимости от того, какие базовые классы реализованы в переданном аргументе.

template <typename T>
void foo(const T& object) {
  if ( /* T inherits from some A<S1> */ )
    std::cout << "object has type A";
  if ( /* T inherits from some B<S2> */ )
    std::cout << "object has type B";
}

Полагаю, я мог бы добавить S1 и S2 в качестве параметров шаблона к foo, но указание их вручную для каждого вызова - большая проблема. Теоретически, если T известно, компилятор должен иметь возможность проверить, наследует ли он A<S>.

Можно ли это сделать?

редактирование:

В дополнение к решению StoryTeller, я использую этот фрагмент кода для приведения моих T -объектов соответственно:

template<template<typename...> class TT, class T>
struct specialization_base_of {

    template<typename... Args>
    static constexpr TT<Args...>  checkVal(TT<Args...> const&);

    static constexpr void checkVal(...);

    template<typename... Args>
    static constexpr TT<Args...>&  checkRef(TT<Args...> const&);

    static constexpr void checkRef(...);

    template<typename... Args>
    static constexpr TT<Args...> const& checkCref(TT<Args...> const&);

    static constexpr void checkCref(...);

    template<typename... Args>
    static constexpr TT<Args...>*  checkPtr(TT<Args...> const&);

    static constexpr void checkPtr(...);

    using value_type = decltype(checkVal(std::declval<T>()));
    using ref_type = decltype(checkRef(std::declval<T>()));
    using cref_type = decltype(checkCref(std::declval<T>()));
    using ptr_type = decltype(checkPtr(std::declval<T>()));
};

1 Ответ

0 голосов
/ 29 апреля 2018

Старый добрый трюк, основанный на разрешении перегрузки, должен работать и здесь:

template<template<typename...> class TT, class T>
struct is_specialization_base_of {
    template<typename... Args>
    static constexpr std::true_type  check(TT<Args...> const&);

    static constexpr std::false_type check(...);

    static constexpr bool value = decltype(check(std::declval<T>()))::value;
};

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

Затем мы определяем две перегрузки, одна - это шаблон, который принимает любую гипотетическую специализацию TT, а вторая - запасной вариант, который является переменной-аргументной функцией в стиле C. Это весь механизм прямо там. Если мы вызываем check с любым классом, который публично наследуется от гипотетической специализации TT, первая перегрузка выбирается из-за того, как работает разрешение перегрузки. В противном случае выбран запасной вариант.

Значение нашего признака определяется типом, созданным в неоцененном контексте decltype, там выполняется разрешение перегрузки, но поскольку это неоцененный контекст, ничего не нужно определять, только объявлять. decltype, следовательно, возвращает тип результата.

Вот простой набор тестов :

struct C1 : A<int> {};
struct C2 : B<int> {};

struct C3 : A<int>, B<char> {};

static_assert(is_specialization_base_of<A, C1>::value);
static_assert(!is_specialization_base_of<B, C1>::value);

static_assert(!is_specialization_base_of<A, C2>::value);
static_assert(is_specialization_base_of<B, C2>::value);

static_assert(is_specialization_base_of<A, C3>::value);
static_assert(is_specialization_base_of<B, C3>::value);

Я оставлю вам упражнение по включению этой черты в вашу функцию шаблона. Я бы просто рекомендовал превратить эти if в if constexpr для некоторых добавленных вкусностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...