类模板继承自不完整的类

3

这段代码因为继承了一个不完整的类型而无法编译通过(https://godbolt.org/z/G35wj9):

template<typename>
class Incomplete;

class T : public Incomplete<T> {};

template<typename>
class Incomplete {};

int main()
{
  [[maybe_unused]] T x;
}

我认为这个规则同样适用于类模板。然而,这段代码可以编译通过(https://godbolt.org/z/cU6GNt):

template<typename>
class Incomplete;

template<int d>
class T : public Incomplete<T<d>> {};

template<typename>
class Incomplete {};

int main()
{
  [[maybe_unused]] T<1> x;
}

当涉及到类模板时,基类只需要在实例化的时候完成吗?

3个回答

4
当涉及到类模板时,基类是否只需要在实例化的点上是完整的?
如果是一个依赖的基类,那么是的。因此,编译器不知道在模板定义的时候Incomplete<T<d>>是什么。毕竟,对于一些d的值,我们可以有一个与主模板声明完全不同的Incomplete<T<d>>的特化。
template<>
class Incomplete<T<0>> {};

这不是循环依赖。 仅仅命名特化为 T<0> 并不会导致它被实例化。它只是一个类型名称。但这意味着编译器必须等待直到它可以检查基类是否有效。
另一方面,如果基类不是相关类型,则使用它作为基类将是不合法的。

3
标准在[temp.inst] §1中涵盖了这个问题(摘自C++17):

除非一个类模板特化已经被显式实例化(17.7.2)或者显式专门化(17.7.3),否则当特化被引用在要求完整定义对象类型的上下文中,或者当类类型的完整性影响程序语义时,类模板特化将被隐式实例化。[: 特别是,如果表达式的语义依赖于类模板特化的成员或基类列表,则类模板特化将被隐式生成。例如,删除指向类类型的指针取决于类是否声明了析构函数,并且指向类类型的指针之间的转换取决于两个涉及类之间的继承关系。—结束注释][示例:

template<class T> class B { /* ... */ };
template<class T> class D : public B<T> { /* ... */ };

void f(void*);
void f(B<int>*);

void g(D<int>* p, D<char>* pp, D<double>* ppp) {
    f(p);            // instantiation of D<int> required: call f(B<int>*)
    B<char>* q = pp; // instantiation of D<char> required: convert D<char>* to B<char>*
    delete ppp;      // instantiation of D<double> required
}

如果一个类模板在实例化的时候已经被声明但是没有定义,那么这个实例化会得到一个不完整的类类型(6.9)。[例子:— 结束例子]

template<class T> class X;
X<char> ch;          // error: incomplete type X<char>

注意:在模板声明中,局部类(12.4)或枚举以及局部类成员从不被视为可以单独实例化的实体(包括它们的默认参数,noexcept-specifiers和非静态数据成员初始化项(如果有)。因此,依赖名称将被查找,语义约束将被检查,并作为局部类或枚举所声明的实体的实例化的一部分来实例化使用的任何模板。此外,在 cppreference 上也有相关内容。

请记住,类模板不是类型,不会生成代码。当模板被实例化时才会生成代码。当类模板被实例化(隐式或显式),实际的类(类型)将被生成(包括其代码)。


1
唯一的区别是,在你的示例中,你在模板内使用了 Incomplete 类。
原始示例中它被一个类使用。类的类型将在此时由编译器创建,因此在 Incomplete 定义之前。
在你的代码中,Incomplete 在另一个模板 T 中使用(粗略地说:T 是通过使用 Incomplete 生成各种类型的模具)。此时编译器什么也不做,它只存储“生成类型规则”(我称之为模具)。
编译器在使用 T 时检查 T 的有效性,因此在该行生成实际类型 T<1>:public Incomplete。
T(模具)“有效”当且仅当 Incomplete 被定义。
[[maybe_unused]] T<1> x;

此时,模板类Incomplete已经定义完毕,编译器可以正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接