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

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

在你的例子中,类型和标量之间的区别是因为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;
// ...
}

可以编译,因为实例化发生在基类已经是完整类型的时候。

最新更新