2012-02-02 3 views
2

Я недавно был удивлен, узнав, что этот код компилируется (по крайней мере, на GCC и MSVC++):Где требуются полные типы (не)?

template<typename T> 
class A { 
public: 
    T getT() { return T(); } 
}; 

class B : public A<B> { }; 

Когда это не делает:

class A; 

class B : public A { }; 

class A { 
public: 
    B getB() { return B(); } 
}; 

Это кажется странным мне, что шаблонный класс может принимать неполный тип в качестве параметра шаблона и иметь функцию, которая возвращала ее, вызывая ее конструктор и все еще компилируясь. Итак, где именно требуются полные типы (или если список будет короче, где они не требуются)?

+0

Это может помочь, если вы думаете о шаблонах не как * код * а как инструмент * генерации кода *, и только фактический код должен сделать семантический смысл. –

+0

@KerrekSB Да, но неясно, как _w__генератор генерации кода генерировал свой код. Теперь я это понимаю. –

ответ

4

Ниже приведены сценарии, в которых Комплектные типы не являются обязательными:

  • Объявление члена быть указатель или ссылку на неполный тип.
  • Объявление функций, которые принимают/возвращают неполные типы.
  • Определение функций, которые принимают/возвращают указатели/ссылки на неполный тип.
  • В качестве шаблона типа аргумент.

В основном Вы прекрасны с помощью Неполное типа, в любом месте, где компилятор не нужно знать расположение памяти в type.

Что касается типа аргумента шаблона разрешают быть неполный тип, стандарт явно говорит так в 14.3.1 аргументов типа шаблона

+0

Я верю 'struct a; a * p; 'и' union b; b * p; 'будет компилироваться отлично, вы просто не хотите разыменовывать указатель. Однако, 'union c; c * p; 'приведет к ошибке компиляции. –

+0

Dereferencing нужен полный тип, поэтому да к первым двум. Тем не менее, я не вижу разницы между третьим выражением и вторым выражением: «Я что-то упустил? –

+0

Проверьте этот вопрос [SO вопрос] (http://stackoverflow.com/questions/71416/forward-declaring-an-enum-in-c). Кроме того, я протестировал на [ideone] (http://ideone.com/bxE5s) и получил следующую ошибку для перечислений. –

4

Вот как работает CRTP из-за двухэтапного разбора шаблонов. Функции члена шаблона не анализируются до их создания.

EDIT: Возможно, формулировка не очень точная. Я хотел сказать, что когда компилятор видит class B : public A<B> {...};, он проходит через A<B>, замечает, что существует функция B get() {...}, но не оценивает его определение, оставив его до фактического экземпляра функции, в результате чего B должен быть полным типом.

EDIT: Я считаю, что точные правила описаны в стандартном разделе 14.6 (как указал Алс в своем ответе). Он имеет имена dependent и non-dependent и их разрешение в разное время во время компиляции в соответствии с поиском двухфазных имен шаблонов. Однако, к сожалению, реализация двухэтапного поиска может отличаться от стандарта для разных компиляторов. Тот же код может компилироваться в GCC и может не отображаться на MSVC++ и наоборот. Более того, один и тот же код может быть отклонен одним и тем же компилятором. В MSVC++ у меня возникла проблема, когда базовый класс использовал указатель на производную функцию класса в качестве аргумента по умолчанию для своей функции. Он не компилировался под MSVC++ и скомпилирован под GCC (правильно). Однако, используя конструктор производного класса как параметр по умолчанию, скомпилированный с обоими компиляторами, даже на MSVC++. Идите фигуру.

+0

Элементы-члены шаблона класса анализируются в точке определения (это причина использования 'typename' и' template', чтобы указать, что элемент является типом или шаблоном соответственно), но они не создаются перед использованием , – Begemoth

+0

Я предполагаю, что я попытался сказать, что определение функции не оценивается на первом этапе разбора. – lapk

+0

Извините, но когда это фактическое создание экземпляра функции? Когда он называется? –

1

Шаблон на самом деле не является кодом; это шаблон , который описывает, как построить код, как только вы заполните недостающие части (параметры типа). Из-за этого компилятор допускает большую свободу действий в определении шаблона, чем фактическое определение кода. Когда вы на самом деле используете шаблон, с указанными типами, компилятор должен сгенерировать фактический код и применить все обычные правила.

Полное определение не требуется, если компилятору не требуется знать размер или смещения членов объекта. Определение указателя или ссылки на класс, например, не нуждается ни в одном из них. В точке, где вы пытаетесь использовать , используйте указатель или ссылку, вам нужно полное определение.

Смежные вопросы