为什么即使直到最后才定义实际类型,依赖名称也可以被认为是完整的

Why can a dependent name be considered as complete even if the actual type is not defined until the very end

Consider this example:

template <class T>
void Yeap(T);

int main() {
    Yeap(0);
    return 0;
}

template <class T>
void YeapImpl();

struct X;

template <class T>
void Yeap(T) {
    YeapImpl<X>(); // pass X to another template
}

template <class T>
void YeapImpl() {
    T().foo();
}

struct X {
    void foo() {}
};

注意 struct X 直到最后才定义。我曾经认为所有 odr 使用的名称在实例化时必须是完整的。但是在这里,编译器如何在定义之前将其视为完整类型?

我已经检查了cppreference中依赖名称和函数模板实例化的绑定规则和查找规则,但是其中none可以解释这里发生的事情。

我认为这个程序格式错误,不需要诊断。

[temp.point]/8阅读,删去无关部分:

A specialization for a function template [...] may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

YeapImpl<X> 有两个实例化点:在问题的注释行和翻译单元末尾调用它的地方。在实例化的第一个点,X 是不完整的,这会使函数的主体格式错误。在实例化的第二个点,X 是完整的,这使得正文是合式的。

这两个专业有[非常]不同的含义。