在编译时检查未实例化的类模板是否继承自其第一个模板参数

Check at compile-time if an uninstantiated class template inherits from its first template parameter

本文关键字:继承 是否 参数 第一个 编译 检查 实例化      更新时间:2023-10-16

简短的问题

如何在编译时检查(编辑:未实例化(类模板是否继承自 C++17 的第一个模板参数?

长问题

我想通过按以下方式构造一些东西来了解类模板是否继承自其(第一个(模板参数:

template<template<typename> class TemplateClass>
struct template_parameter_is_base_of
{
/* Implementation */
};

理想情况下,template_parameter_is_base_of可以像这样使用:

template<typename T> struct WithParent : T { /* Implementation */ };
template<typename T> struct WoutParent :   { /* Implementation */ };
static_assert(template_parameter_is_base_of<WithParent>::value); // Ok as expected since WithParent inherits from T
static_assert(template_parameter_is_base_of<WoutParent>::value); // Error as expected since WithParent doesn't inherit from T

我失败的尝试

我尝试实现template_parameter_is_base_of

struct Dummy {};
template<template<typename> class TemplateClass>
struct template_parameter_is_base_of
{
static constexpr bool value = std::is_base_of_v<Dummy, TemplateClass<Dummy>>;
};

。在这种情况下有效:

template<typename T>
struct A : T {
void work() { /* Irrelevant implementation */ }
};
static_assert(template_parameter_is_base_of<A>::value); // Passes because A inherits from its template parameter. Nice!

。但是,如果类模板具有带有override说明符的方法,则失败:

template<typename T>
struct B : T {
void work() override { /* Irrelevant implementation */ }
};
static_assert(template_parameter_is_base_of<B>::value); // Fails, but I want it to pass because B inherits from its template parameter.

这就是我现在的想法

我想我已经用上面使用Dummy的方法把自己画进了一个角落,因为如果TemplateClass包含任何带有override说明符的方法,则std::is_base_of_vTemplateClass<Dummy>的类模板实例化将始终失败。

但是,我认为实现template_parameter_is_base_of应该是可能的,因为编译器应该在编译时知道类模板是否继承自其模板参数。也许我错了。

最后的问题

是否可以从 C++17 开始实施template_parameter_is_base_of?如果是,怎么能做到这一点?

但是,我认为实现template_parameter_is_base_of应该是可能的,因为编译器应该在编译时知道模板类是否继承自其模板参数。

模板本质上是一个参数化工具,用于制造某些C++构造:类、函数或变量。模板本身还不是它要制作的东西。类模板不会从任何内容继承,因为它还不是类。所以这个问题本身不是一个功能性的问题。

除此之外,还存在显式/部分模板专用化。即使基类模板确实继承了其第一个模板参数,也不能保证任何事情。您仍然不知道模板的任何特定WithParent<T>实例化是否实际使用基本模板。用户可以轻松地针对特定类型进行WithParent专用化,甚至可以对整个类型系列采用部分专用化。

你想要的不是C++可以支持的东西。如果你试图验证某些东西或防止某些滥用或其他什么,你将不得不以另一种方式去做。

如何在编译时检查类模板是否继承自 C++17 的第一个模板参数?

您的意思是,如果实例化的类模板继承自其第一个模板参数?

简单,使用std::is_base_of_v

template <typename T, /*other stuff goes here*/>
class A : /* classes which A inherits from */ { /* ... */}
template <typename T, /*other stuff goes here*/>
constexpr bool A_inherits_its_first_tempalte_param() {
return std::is_base_of_v<T, A<T, /* ... */ >;
}

就是这样。

不确定是否是一个好方法,但我建议您采用以下方式进行探索。

您可以尝试编写类型特征,如下所示

template <typename T>
void foo (T *);
template <template <typename> class, typename = void>
struct tp_is_base_of : public std::false_type
{ };
template <template <typename> class C>
struct tp_is_base_of<
C, std::void_t<decltype(foo<Dummy>(std::declval<C<Dummy>*>()))>>
: public std::true_type
{ };

通过这种方式,您可以检查等待Dummy指针的函数是否接受C<Dummy>指针:如果C<Dummy>继承自Dummy,答案应该是肯定的。

以下是完整的编译示例

#include <type_traits>
struct Dummy { virtual void work() {} };
template <typename T>
struct A : T
{ void work() { /* Irrelevant implementation */ } };
template <typename T>
struct B : T
{ void work() override { /* Irrelevant implementation */ } };
template <typename T>
struct C
{ };
template <typename T>
void foo (T *);
template <template <typename> class, typename = void>
struct tp_is_base_of : public std::false_type
{ };
template <template <typename> class C>
struct tp_is_base_of<
C, std::void_t<decltype(foo<Dummy>(std::declval<C<Dummy>*>()))>>
: public std::true_type
{ };
int main()
{
static_assert( true  == tp_is_base_of<A>::value );
static_assert( true  == tp_is_base_of<B>::value );
static_assert( false == tp_is_base_of<C>::value );
}