Почему зависимое имя может считаться полным, даже если фактический тип не определен до самого конца

Рассмотрим этот пример:

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 является полным, что делает тело хорошо сформированным.

Эти две специализации имеют [очень] разные значения.

Другие вопросы по тегам