【问题标题】:CRTP: Pass types from derived class to base classCRTP:将类型从派生类传递到基类
【发布时间】:2021-10-25 11:50:15
【问题描述】:

CRTP 中,基类可以使用派生类中的函数和变量。但是,派生类的类型不能被基类直接使用,代码如下:

#include <iostream>

template <class Derived>
class A {
public:
    //using Scalar = typename Derived::Scalar; // Error!
    static constexpr int NA1 = Derived::NB1;
    static constexpr int NA2 = Derived::NB2;
    static constexpr int NA3 = Derived::NB3;
};

template <int _N = 2>
class B : public A<B<_N>> {
public:
    using Scalar = double;
    static constexpr int NB1 = 1;
    static constexpr int NB2 = _N;
    static constexpr int NB3 { sizeof(Scalar) };
};

int main(int argc, char** argv)
{
    using Type = B<2>;
    std::cout << Type::NA1 << ' '
              << Type::NA2 << ' '
              << Type::NA3 << '\n';
}

// output:
// 1 2 8

如果using Scalar = typename Derived::Scalar;这行没有注释,就会报错:

main.cpp:6:11: error: invalid use of incomplete type 'class B<2>'

我知道类型(Scalar)可以作为模板参数传递给基类,但是为什么不能像变量一样使用呢?这只是语言规则吗?还是有什么逻辑上的限制让这个无法实现?

【问题讨论】:

    标签: c++ templates crtp


    【解决方案1】:

    A 中,B 是一个不完整的类型——编译器还没有看到B 的完整声明,所以你不能在A 的声明中使用Scalar。这是自然的限制。

    在您的示例中,类型和标量之间存在差异,因为 NAinitialization 的实例化不是在声明时发生,而是在编译器看到 B 之后发生(并成为一个完整的类型)。

    让我们更改代码并强制编译器在类声明中使用NA 值:

    template <class Derived>
    class A {
    public:
        static constexpr int NA1 = Derived::NB1;
    
        std::array<int, NA1> foo();
    };
    

    现在你会得到基本相同的错误:

    <source>:8:41: error: incomplete type 'B<2>' used in nested name specifier
        8 |     static constexpr int NA1 = Derived::NB1;
          |                                         ^~~
    

    这类似于成员函数:您不能在其声明中使用 CRTP 基类型,但您可以在其主体中使用该类型:

    void foo() {
        std::array<int, NA1> arr;
        // ...
    }
    

    将编译,因为实例化发生在基类已经是完整类型的位置。

    【讨论】:

    • 我的印象是 NA* 都是用依赖名称初始化的,因此两阶段查找意味着 Derived 在实例化发生时实际上已被编译器绑定到 B。似乎我在那里做了一些不正确的假设。删除 static 和/或 constexpr 会使我的假设正确吗?
    • @TanveerBadar 回答这个问题需要语言律师的帮助。如果我正确地回答了您的问题,我怀疑答案是否定的,因为A 中的Derived 仍然是不完整的类型。 A 的实例化必须始终在B 的实例化之前,因为AB 的基类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多