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) 可以作为模板参数传递给基类,但为什么不能像变量一样使用它呢?这只是语言规则吗?或者是否有任何逻辑限制使这无法实现?

回答

Inside A, B is an incomplete type - a compiler hasn't yet seen the complete declaration of B, so you can't use Scalar inside the declaration of A. This is a natural restriction.

The difference between a type and a scalar in your example arises because instantiation of initialization of NA happens not at the point of declaration, but only after B was seen by a compiler (and became a complete type).

Let's change the code and force a compiler to use NA value inside the class declaration:

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

Now you'll get essentially the same error:

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

This is similar to member functions: you can't use a CRTP base type in their declarations, but you can use that type in their bodies:

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

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


以上是CRTP:将类型从派生类传递到基类的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>