C++17:对参数包中的模板进行通用(基于多重继承?)检查
C++17: Generic (multiple-inheritance based?) check for template in parameter pack
我需要一些代码来检查某个模板是否是参数包的一部分。为了实现对普通类的检查,我使用了基于多重继承的方法,例如路易斯·迪翁(Louis Dionne)在这里或奥古斯丁·贝尔热(Agustín Bergé)在这里概述的方法。
类测试
这个想法是,您将包中的每个类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>
等编写单独的代码。
如果可以使用相同的多重继承技巧来完成,则奖励积分。
正如@MaxLanghof评论中提到的,甚至不可能声明接受任意类型模板的has_tmpl
。可以使用不同的模板参数(template<std::size_t> class
、template<std::size_t, typename> class
、template <typename, typename> class
等)重载has_tmpl
,但需要无限数量的重载。
相反,让我们使用包装模板的类型,并公开我们需要的一切。最简单的 AFAIK 方法是 (ab) 使用 lambda:[]<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>>){}));
}
请注意,这使用在 C++20 中标准化的 GNU 扩展(通用 lambda 的模板参数列表)。我认为这是无法避免的。
应该可以使用多重继承,但折叠表达;)
我希望这就是您正在寻找的(我实现了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>());
}
- 关于C++中具有多重继承"this"指针的说明
- C++中模板化异常类的多重继承
- 虚拟继承中是否存在多重继承?
- 如何在 c++ 多重继承中调用父非虚函数?
- 多重继承相同的方法名,没有歧义
- 使用enable_if解决多重继承歧义
- 多重继承导致虚假的模糊虚拟函数过载
- 多重继承和访问不明确的元素
- C++ 多重继承:使用基类 A 的实现实现基类 B 的抽象方法
- 多重继承中的派生类的行为类似于聚合
- 为什么我的 Hippomock 期望在使用多重继承时失败
- 带有此指针的模板类多重继承构造函数不起作用?
- 使用多重继承时出现编译错误
- 增强多重继承的序列化
- 多重继承:跳过'virtual'关键字并拒绝菱形层次结构的使用?
- 仅函数的多重继承 - 没有虚拟和 CRTP
- C++多重继承和鸭子类型
- C++两次从文件保存对象读取多重继承
- C++17:对参数包中的模板进行通用(基于多重继承?)检查
- 检查类型是否使用多重继承