首页 > 解决方案 > CRTP:将类型从派生类传递到基类

问题描述

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++templatescrtp

解决方案


Inside A,B是一个不完整的类型 - 编译器还没有看到 的完整声明B,所以你不能ScalarA. 这是一种自然的限制。

您的示例中类型和标量之间的区别在于,初始化的实例化NA不是在声明时发生,而是在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;
    // ...
}

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


推荐阅读