6

假设我有以下代码片段:

template <class T>
class Bar
{
    // static_assert(sizeof(T) > 0); // (1)
public:
    void method()
    {
        static_assert(sizeof(T) > 0); // (2)
    }
};

class Foo; // (3)

template class Bar<Foo>; // (4)

class Foo{}; // (5)

如果我们取消注释第 (1) 行,我们会得到一个编译时错误“不完整类型 T”,并且似乎很清楚:class Bar实例化是由 (4) 启动的,而此时class Foo仅由 (3) 前向声明尚未由 (5) 定义。

但是如果第(1)行被注释掉了,那么这段代码编译没有错误,这让我很困惑:(4)是一个显式的模板实例化定义,它强制编译器生成void method()代码,第(2)行也应该生成同样的错误,因为 的定义Foo稍后在 (5) 中进行。

我错过了什么,为什么片段中的代码会编译?

更新:代码在 GCC 8.2.0 和 MSVC 19.16.27025.1 下编译,但在 Clang 7.0.0 下它给出“不完整类型”错误。

4

1 回答 1

7

根据标准,在隐式实例化期间,仅实例化成员函数的声明,而不实例化它们的定义。

[temp.inst]/2 - 类模板特化的隐式实例化导致

  • 未删除的类成员函数、成员类、作用域成员枚举、静态数据成员、成员模板和朋友的声明的隐式实例化,但不是定义的隐式实例化......

但对于显式实例化,它并没有说同样的话。整个类都被实例化了,这意味着它实例化了的定义,method()并且在这一点上Foo是不完整的。

[temp.explicit]/11 - 命名类模板特化的显式实例化定义显式实例化类模板特化,并且是仅在实例化点已定义的那些成员的显式实例化定义。

clang 拒绝代码。

于 2019-01-01T18:24:14.737 回答