【问题标题】:Constrained CRTP Premature Rejection受约束的 CRTP 过早拒绝
【发布时间】:2021-09-24 19:25:53
【问题描述】:

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

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

struct D : public S<D>
{
};

这在 gcc、clang 和 msvc 上也能很好地编译和运行。现在,我想“确保”模板参数继承自基类:

#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 在必须检查约束时尚未完全定义,因此它无法代替必要的信息。也就是说,我有点失望,因为在评估尚未检查的约束之前没有尝试完成派生类的定义。

这种行为是有意的吗?还有其他方法可以检查实际有效的继承吗?

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

【问题讨论】:

    标签: c++ templates c++-concepts incomplete-type


    【解决方案1】:

    可以在基类的默认构造函数中查看需求

    #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 是在构造函数被实例化时完全定义的。

    【讨论】:

    • 请注意还有std::derived_from这个概念,所以你也可以使用requires子句。
    • @Bob__ 是的,这些工作。然而,与模板头不同,约束构造函数对用户来说不太清楚,生成的错误消息也不太有用。不要误会我的意思,我很感谢你的回答,但是这个问题几乎就是为什么将概念引入到语言中,如果这个例子不能用它们来解决,那将是非常令人失望的。
    • @KelemenMáté 我同意,而且这不适用于static 方法,但我认为static_assert 也是唯一的方法。在 CRTP 中,模板参数将始终是不完整的类型。我认为这是设计模式的缺陷,而不是概念本身的缺陷。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 2018-12-15
    • 1970-01-01
    相关资源
    最近更新 更多