哪些是完整类型(不是)需要?

时间:2012-02-02 04:00:28

标签: c++ incomplete-type

我最近惊讶地发现这段代码编译(至少在gcc和MSVC ++上):

template<typename T>
class A {
public:
    T getT() { return T(); }
};

class B : public A<B> { };

如果没有:

class A;

class B : public A { };

class A {
public:
    B getB() { return B(); }
};

对我来说,模板类可以将不完整的类型作为模板参数,并且有一个函数通过调用其构造函数返回一个并仍然编译,这似乎很奇怪。那么究竟什么是完整的类型(或者如果列表更短,哪些不需要)?

3 个答案:

答案 0 :(得分:4)

由于两阶段模板解析,这就是CRTP的工作原理。模板成员函数在实例化之前不会被解析。

编辑:也许,措辞不是很精确。当编译器看到时,我的意思是说 class B : public A< B > {...};,它经过A< B >,注意到有一个函数B get() {...},但没有评估它的定义,只留下函数的实际实例化,此时B有是一个完整的类型。

编辑:我相信,确切的规则在标准第14.6节中有所涉及(正如Als在他的回答中指出的那样)。它根据两阶段模板名称查找在编译期间的不同时间处理dependentnon-dependent名称及其解析。但是,遗憾的是,两阶段名称查找实现可能与不同编译器上的标准不同。相同的代码可能在GCC上编译,可能不在MSVC ++上编译,反之亦然。更重要的是,相同的编译器可能会拒绝看似相同的代码。在MSVC ++上,当基类使用指向派生类函数的指针作为其函数的默认参数时,我遇到了一个问题。它没有在MSVC ++下编译并在GCC(正确)下编译。但是,使用派生类构造函数作为使用两个编译器编译的默认参数,即使在MSVC ++上也是如此。去图。

答案 1 :(得分:4)

以下是不需要完整类型的方案:

  • 声明某个成员是指针或对不完整类型的引用。
  • 声明接受/返回不完整类型的函数。
  • 定义接受/返回指向不完整类型的指针/引用的函数。
  • 作为模板类型参数。

基本上你可以在编译器不需要知道type的内存布局的任何地方使用不完整类型。

对于允许为不完整类型的模板类型参数,标准在 14.3.1模板类型参数中明确说明

答案 2 :(得分:1)

模板不是真正的代码;它是一个模板,它描述了如何构建代码,一旦填写了缺失的部分(类型参数)。因此,编译器在模板定义中允许比实际代码定义更多的余地。当您实际使用模板时,如果标识了类型,则编译器需要生成实际代码并应用所有常用规则。

如果编译器不需要知道对象成员的大小或偏移量,则不需要完整定义。例如,定义指针或对类的引用不需要其中任何一个。在您尝试使用指针或引用时,您需要一个完整的定义。

相关问题