C ++ 17: Общая проверка (на основе множественного наследования?) Для шаблона в пакете параметров - PullRequest
9 голосов
/ 19 июня 2019

Мне нужен код, чтобы проверить, является ли определенный шаблон частью пакета параметров.Чтобы реализовать проверку для нормальных классов, я использую подход, основанный на множественном наследовании, описанный, например, Луи Дионн здесь или Агустин Берже здесь .

Тестирование для класса

Идея состоит в том, что вы упаковываете каждый класс T в пакете в класс PackEntry, а затем PackIndex наследует от всех PackEntry классов,Таким образом, если вы ищете класс A, все, что вам нужно сделать, это проверить, можно ли преобразовать PackIndex в правильный PackEntry.Сводя все воедино, это выглядит так:

#include <cstddef>
#include <utility>

template <class T>
struct PackEntry
{
    using type = T;
};

template <class... Ts>
struct PackIndex : PackEntry<Ts>...
{};

template <class T, class... Ts>
struct PackSearcher
{
    static constexpr std::false_type check(...);

    // This overload is used if the PackIndex below 
    // derives from PackEntry<T>, the overload above 
    // otherwise. 
    static constexpr std::true_type check(PackEntry<T>);

    using type =
        decltype(check(PackIndex<Ts...>{}));

    static constexpr bool
    value()
    {
        return type{};
    }
};

template <class... Ts>
struct Pack
{
    template<class T>
    static constexpr bool has() {
        return PackSearcher<T, Ts...>::value();
    }
};

int main() {
    static_assert(Pack<int, void>::has<int>());
    static_assert(! Pack<int, void>::has<bool>());
}

Тестирование для определенного шаблона

Теперь это все аккуратно, но скажите, что у меня есть шаблон Tmpl, и я хочучтобы узнать, содержит ли мой пакет любую специализацию этого шаблона.Это легко сделать, если шаблон имеет определенную форму, скажем, template <std::size_t> class Tmpl {};. По сути, единственное отличие состоит в том, что внутри PackSearcher (который я сейчас называю TmplPackSearcher, вы заставляете компилятор получать правильный параметр шаблона N дляшаблон.

#include <cstddef>
#include <utility>

template <class T>
struct PackEntry
{
    using type = T;
};

template <class... Ts>
struct PackIndex : PackEntry<Ts>...
{
};

template <template <std::size_t> class T, class... Ts>
struct TmplPackSearcher
{ 
    static constexpr std::false_type check(...);

    template <std::size_t N>
    static constexpr std::true_type check(PackEntry<T<N>>);

    using type =
        decltype(check(PackIndex<Ts...>{}));

    static constexpr bool
    value()
    {
        return type{};
    }
};

template <class... Ts>
struct Pack
{
    template<template <std::size_t> class T>
    static constexpr bool has_tmpl() {
        return TmplPackSearcher<T, Ts...>::value();
    }
};

template<std::size_t I>
class Tmpl {};

int main() {
    static_assert(Pack<Tmpl<1>, int>::has_tmpl<Tmpl>());
    static_assert(!Pack<int>::has_tmpl<Tmpl>());
}

Вопрос

Теперь мой вопрос: как проверить наличие шаблона в пакете параметров , не делая никаких предположений о том, как шаблонвыглядит как ? То есть я не хочу писать отдельный код для template<std::size_t>, template<std::size_t, typename>, template <typename, typename> и т. д.

Бонусные баллы, если это можно сделать с использованием одного и того жеТрюк с наследованием.

Ответы [ 2 ]

3 голосов
/ 20 июня 2019

Как упоминалось в комментариях @MaxLanghof, невозможно даже объявить has_tmpl, который принимает произвольные типы шаблонов. Возможно иметь перегрузки has_tmpl с различными параметрами шаблона (template<std::size_t> class, template<std::size_t, typename> class, template <typename, typename> class и т. Д.), Но необходимо бесконечное количество перегрузок.

Вместо этого давайте воспользуемся типом, который обертывает шаблон, и выставим все, что нам нужно. Простейший способ AFAIK - (ab) использовать лямбду: []<std::size_t I>(type_identity<Tmpl<I>>){}.

И тогда проблема почти тривиальна: можно просто определить has_tmpl, чтобы вернуть (std::is_invocable_v<Lambda,type_identity<Ts>> || ...).

Полный пример:

#include <type_traits>

template<class> struct type_identity {};

template <class... Ts>
struct Pack
{
    template<class Lambda>
    static constexpr bool has_tmpl(Lambda) {
        return (std::is_invocable_v<Lambda, type_identity<Ts>> || ...);
    }
};

template<std::size_t I>
class Tmpl {};

int main() {
    static_assert(Pack<Tmpl<1>, int>::has_tmpl([]<std::size_t I>(type_identity<Tmpl<I>>){}));
    static_assert(!Pack<int>::has_tmpl([]<std::size_t I>(type_identity<Tmpl<I>>){}));
}

Обратите внимание, что здесь используется расширение GNU, стандартизированное в C ++ 20 (список-параметров-шаблонов для общих лямбд). Я не думаю, что этого можно избежать.

Должно быть возможно использовать множественное наследование, но выражение сгиба намного короче;)

1 голос
/ 19 июня 2019

Надеюсь, это то, что вы ищете (я реализовал класс Pack, не проверяя ваш трюк наследования).

#include <type_traits>

// just for testing
template <typename... Ts> struct Foo {};
template <typename... Ts> struct Bar {};

// compare templates
template <template <typename...> typename, typename>
struct is_same : std::false_type {};

template <template <typename...> typename T, typename... Ts>
struct is_same<T, T<Ts...>> : std::true_type {};

// find templates in list
template <template <typename...> typename T, typename... Pack>
struct searcher
    : std::integral_constant<bool, std::disjunction_v<is_same<T, Pack>...>> {};

// your class (only template params changed)
template <class... Ts>
struct Pack {
  template <template <typename...> class T>
  static constexpr bool has_tmpl() {
    return searcher<T, Ts...>::value;
  }
};

int main() {
  static_assert(Pack<int, long, Foo<int>>::has_tmpl<Foo>());
  static_assert(Pack<int, long, Foo<int, short>>::has_tmpl<Foo>());
  static_assert(Pack<int, long, Foo<int, short, long>>::has_tmpl<Foo>());

  static_assert(!Pack<int, long, long>::has_tmpl<Foo>());
  static_assert(!Pack<int, long, Bar<int>>::has_tmpl<Foo>());
  static_assert(!Pack<int, long, Bar<int, short>>::has_tmpl<Foo>());
  static_assert(!Pack<int, long, Bar<int, short, long>>::has_tmpl<Foo>());
}
...