Почему зависимое имя может считаться полным, даже если фактический тип не определен до самого конца
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, но ни один из них не может объяснить, что здесь происходит.
1 ответ
Я считаю, что эта программа плохо сформирована, диагностика не требуется.
[temp.point] / 8 читает, редактируя несущественные части:
Специализация для шаблона функции [...] может иметь несколько точек создания экземпляров в единице перевода, и в дополнение к описанным выше точкам создания экземпляров для любой такой специализации, которая имеет точку создания в единице перевода, конец единицы перевода также считается точкой создания экземпляра. [...] Если две разные точки инстанцирования придают специализации шаблона разные значения в соответствии с правилом единого определения, программа некорректна, диагностика не требуется.
YeapImpl<X>
имеет две точки инстанцирования: где он вызывается в строке комментария в вопросе и в конце блока перевода. В первой точке создания, X
является неполным, что сделало бы тело функции плохо сформированным. Во втором пункте реализации, X
является полным, что делает тело хорошо сформированным.
Эти две специализации имеют [очень] разные значения.