Где требуются полные типы (не)? - PullRequest
2 голосов
/ 02 февраля 2012

Недавно я был удивлен, узнав, что этот код компилируется (по крайней мере, для 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(); }
};

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

Ответы [ 3 ]

4 голосов
/ 02 февраля 2012

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

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

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

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

4 голосов
/ 02 февраля 2012

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

РЕДАКТИРОВАТЬ: Может быть, формулировка не очень точная. Что я хотел сказать, когда компилятор видит class B : public A< B > {...};, он проходит через A< B >, замечает, что есть функция B get() {...}, но не оценивает ее определение, оставляя его до фактического создания экземпляра функции, когда B должен быть завершенным типом.

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

1 голос
/ 02 февраля 2012

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

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

...