Неполное использование класса в шаблоне

Я очень удивлен тем, что в различных образцах версии g++ следующие компиляции без ошибок или предупреждений:

// Adapted from boost::checked_delete()
template <class T> inline void assert_complete()
{
  typedef char type_must_be_complete[ sizeof(T) ? 1 : -1 ];
  (void) sizeof(type_must_be_complete);
}

class X;

void f()
{
  assert_complete<X>();
}

class X {};

int main() {}

Если определение X отсутствует или в другой единицы перевода, я получаю ошибки.

Но в программе, как указано выше, не является ли определение f единственной точкой экземпляра моего шаблона? И не является ли неполнота X в этой точке инстанцирования семантической ошибкой?

Соответствует ли стандарту (С++ 03 и/или С++ 11 Draft) эта программа хорошо сформированной, плохо сформированной, плохо сформированной, но диагностической не требуется, или undefined поведение?

Редактировать: @David Rodriguez - dribeas сообщает, что clang++, goau и Visual Studio 2010 также принимают аналогичный код.

Ответ 1

(Я ожидал, что Альф Штайнбах опубликует ответ, но поскольку он этого не делает, я отправлю ссылку, которую он упомянул в чате Lounge С++):

Стандарт указывает, что экземпляры шаблонов выполняются после того, как единица перевода уже была переведена, так что вовремя экземпляры шаблонов происходят после того, как все незатемные элементы уже обработаны. Это описано в разделе 2.2 Фазы перевода:

Пункты 1-6 определяют работу препроцессора и основные текстовые операции (преобразования набора символов, конкатенация литералов...)

7/Символы белого пробела, разделяющие токены, уже не являются значимыми. Каждый токен предварительной обработки преобразуется в токен. (2.7). Результирующие маркеры синтаксически и семантически анализируются и переводятся как единица перевода.

8/Переводные единицы перевода и единицы экземпляра объединяются следующим образом: каждая переведенная единица перевода исследуется для создания списка необходимых экземпляров. Определены требуемые шаблоны. Определяется реализация, требуется ли источник блоков перевода, содержащих эти определения. Все необходимые экземпляры выполняются для создания единиц экземпляра. [Примечание: они похожи на переведенные единицы перевода, но не содержат ссылок на неинсталлированные шаблоны и определения шаблонов. - конец примечания] Программа плохо сформирована, если какая-либо инстанция не удалась.

Я сократил некоторые примечания для краткости. Теперь важный бит, кажется, заключается в том, что код переведен без запуска экземпляров шаблонов за один шаг, а затем на более позднем этапе создаются шаблоны. Это, в свою очередь, означает, что если тип завершен в любом месте единицы перевода, он будет обработан к тому времени, когда компилятор достигнет экземпляра.

Отказ от ответственности: это кажется хорошей причиной для всех компиляторов, которые я пробовал, демонстрируя то же самое поведение (gcc, clang, goau, VS 2010), но это только указывает, когда со временем выполняется инстанцирование, явно не указывают, что тип может быть неполным в момент создания шаблона.

Ответ 2

Эта строка завершает тип:

class X {};

Пока тип завершается где-то в блоке перевода, будут завершены все более ранние неполные экземпляры.

Здесь соответствующий раздел из стандартного [basic.types] (пункт 3.9 п. 7):

Тип класса (например, "класс X" ) может быть неполным в одной точке единицы перевода и заканчиваться позже; тип "класс X" является одним и тем же типом в обеих точках. Объявленный тип объекта массива может быть массивом неполного типа класса и, следовательно, неполным; если тип класса будет завершен позже в блоке перевода, тип массива будет завершен; тип массива в этих двух точках является одним и тем же типом. Объявленный тип объекта массива может быть массивом неизвестного размера и поэтому быть неполным в одной точке единицы перевода и заканчиваться позже; типы массивов в этих двух точках ( "массив неизвестной границы T" и "массив N T" ) - это разные типы. Тип указателя на массив неизвестного размера или тип, определяемый объявлением typedef, как массив неизвестного размера, не может быть завершен.