约束 CRTP 过早拒绝

Constrained CRTP Premature Rejection

我正在尝试实现从基本模板继承的派生 class,并将派生 class 作为其模板参数(希望下面的示例能够解决问题):

template <class T>
struct S
{
    T f() {return T();}
};

struct D : public S<D>
{
};

这在 gcc、clang 和 msvc 上编译和运行良好。现在,我想“确保”模板参数继承自基础 class:

#include <concepts>

template <class T>
concept C
= requires ( T t )
{
    { t.f() };
};

template <C T>
struct S
{
    T f() {return T();}
};

struct D : public S<D>
{
};

然而,这会被每个编译器拒绝,其中 clang 提供了最深刻的见解:

error: constraints not satisfied for class template 'S' [with T = D]
struct D : public S<D>
                  ^~~~
note: because 'D' does not satisfy 'C'
template <C T>
          ^
note: because 't.f()' would be invalid: member access into incomplete type 'D'
    { t.f() };

我了解编译器的来源:D 在必须检查约束时尚未完全定义,因此它会失败以代替必要的信息。也就是说,我有点失望,因为在评估一个尚未检查的约束之前,没有尝试完成派生 class 的定义。

这种行为是故意的吗?是否有另一种方法来检查实际有效的继承?

顺便说一下,在这种情况下,gcc 给出了相当无用的 error message

您可以在基础的默认构造函数中检查需求class

#include <type_traits>

template<class Derived>
class Base
{
public:
    Base()
    {
        static_assert(std::is_base_of_v<Base<Derived>, Derived>);
    }
};

class Derived : public Base<Derived>
{ };

这也必须在任何其他用户定义的基类的非复制和非移动构造函数中进行检查。这是有效的,因为 Derived 在实例化构造函数时已完全定义。