Неполное использование класса в шаблоне - PullRequest
14 голосов
/ 27 августа 2011

Я очень удивлен, что на различных выборочных версиях 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 в этой точке реализации семантической ошибкой?

Стандарт (C ++ 03 и / или C ++ 11 Draft) называет эту программу правильно сформированной, плохо сформированной, плохо сформированной, но не требующей диагностики или неопределенным поведением?

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

Ответы [ 2 ]

12 голосов
/ 26 октября 2011

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

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

В параграфах 1-6 определяется работа препроцессора и основные текстовые операции (преобразования набора символов, объединение литералов ...)

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

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

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

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

6 голосов
/ 27 августа 2011

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

class X {};

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

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

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

...