CRTP:将类型从派生 class 传递到基础 class
CRTP: Pass types from derived class to base class
在CRTP中,基础class可以使用派生class的函数和变量。但是,derived class 的类型不能被base class 直接使用,参见下面的代码:
#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
)可以作为模板参数传递给base class,但为什么不能像变量一样使用呢?这只是语言规则吗?或者是有什么逻辑上的限制导致这个无法实现?
在A
里面,B
是一个不完整的类型——编译器还没有看到B
的完整声明,所以你不能使用Scalar
在 A
的声明中。这是天生的限制。
您的示例中类型和标量之间的区别是因为 NA
的 初始化 的实例化不是在声明时发生,而是仅在 [= 之后发生14=] 被编译器看到(并成为一个完整的类型)。
让我们更改代码并强制编译器在 class 声明中使用 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;
// ...
}
将编译,因为实例化发生在基 class 已经是完整类型的地方。
在CRTP中,基础class可以使用派生class的函数和变量。但是,derived class 的类型不能被base class 直接使用,参见下面的代码:
#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
)可以作为模板参数传递给base class,但为什么不能像变量一样使用呢?这只是语言规则吗?或者是有什么逻辑上的限制导致这个无法实现?
在A
里面,B
是一个不完整的类型——编译器还没有看到B
的完整声明,所以你不能使用Scalar
在 A
的声明中。这是天生的限制。
您的示例中类型和标量之间的区别是因为 NA
的 初始化 的实例化不是在声明时发生,而是仅在 [= 之后发生14=] 被编译器看到(并成为一个完整的类型)。
让我们更改代码并强制编译器在 class 声明中使用 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;
// ...
}
将编译,因为实例化发生在基 class 已经是完整类型的地方。