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

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

简短的问题

如果 (edit: 未实例化) class 模板从 C++17 的第一个模板参数继承,我如何在编译时检查?

长问题

我想找出 class 模板是否继承自其(第一个)模板参数,方法是按照以下行构建一些内容:

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!

...但如果 class 模板具有带有 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 方法把自己逼到了一个角落,因为 std::is_base_of_vTemplateClass<Dummy> 的 class 模板实例化总是会失败,如果TemplateClass 包含任何带有 override 说明符的方法。

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

终于有问题了

是否可以从 C++17 开始实现 template_parameter_is_base_of?如果是,那该怎么做?

However, I think that implementing template_parameter_is_base_of should be possible because a compiler should know at compile-time whether a template class inherits from its template parameter or not.

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

此外还有 explicit/partial 模板专业化存在的事实。即使基本 class 模板确实继承了它的第一个模板参数,也不能保证任何事情。您仍然不知道模板的任何特定 WithParent<T> 实例化是否会实际使用基本模板。用户可以轻松地将 WithParent 专门化为特定类型,甚至可以对整个类型系列使用部分专门化。

你要的不是C++能支持的。如果你想验证某事或防止某些滥用或其他任何事情,你将不得不以另一种方式进行。

How could I check at compile-time if a class template inherits from its first template parameter as of C++17?

你的意思是,如果一个 实例化 class 模板继承自它的第一个模板参数?

简单,使用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
 { };

通过这种方式检查 C<Dummy> 指针是否被等待 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 );
 }