C++17:泛型(基于多重继承?)检查参数包中的模板

C++17: Generic (multiple-inheritance based?) check for template in parameter pack

我需要一些代码来检查某个模板是否是参数包的一部分。为了实现对正常 classes 的检查,我使用了基于多重继承的方法,例如通过 Louis Dionne here or Agustín Bergé here

测试 Class

我们的想法是将包中的每个 class T 包装在 PackEntry class 中,然后让 PackIndex 继承所有 PackEntry classes。这样,如果您要查找 class 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 {}; 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> 等编写单独的代码

如果可以使用相同的多重继承技巧完成,则加分。

我希望这就是您要找的(我实现了 Pack class,但没有测试您的继承 "trick")。

#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>());
}

正如@MaxLanghof 在评论中提到的,甚至不可能声明一个接受任意类型模板的 has_tmpl。可以使用不同的模板参数(template<std::size_t> classtemplate<std::size_t, typename> classtemplate <typename, typename> class 等)重载 has_tmpl,但需要无限数量的重载。

相反,让我们使用包装模板的类型,并公开我们需要的一切。 AFAIK 最简单的方法是(ab)使用 lambda:[]<std::size_t I>(type_identity<Tmpl<I>>){}.

然后问题几乎是微不足道的:has_tmpl 可以简单地定义为 return (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>>){}));
}

请注意,这使用了在 C++20 中标准化的 GNU 扩展(template-parameter-list 用于通用 lambda)。我不认为这是可以避免的。

应该可以使用多重继承,但折叠表达式要短得多;)