Неполный тип – это элемент, который сообщает компилятору, что существует тип с таким именем, но ничего не говорит ему о том, как он реализован: какие есть функции, переменные и т. д. Обычно эти типы полностью определяются позже, поэтому такое объявление часто называют опережающим (forward declaration).
К неполным типам относятся:
Чтобы тип стал полным (полностью определенным) необходимо указать недостающую информацию. Отдельно стоит отметить тип void, который не может быть полным в принципе.
Так как неполный тип ничего не говорит компилятору о своём внутреннем устройстве, то не получится скомпилировать код, который пытается получить доступ к его содержимому. Также невозможно будет выполнить операции, требующие знания точного размера типа. Для этого необходимо знать размер типов, которые содержатся в требуемом классе.
Неполный тип можно получить при использовании следующих конструкций языка:
Опережающая декларация (forward-declaration):
class MyType;
Указатель на неизвестный тип:
struct MyType *myPtr;
Массив, содержащий элементы неопределённого типа (даже если количество элементов известно):
MyType b[10];
Во всех этих случаях, для полного определения типа, мы обязаны предоставить где-нибудь реализацию типа 'MyType'. Например, такую:
class MyType {
int someNumber;
}
В таком случае все ограничения, накладываемые на неполные типы, будут сняты.
Отдельного разъяснения заслуживает ситуация с массивами неопределённого размера, т. к. при их использовании есть несколько нюансов. Например:
extern int a[]; // Неполный тип (массив неизвестного
// размера с элементами типа 'int')
int b[] = { 1, 2, 3 }; // Полный тип (массив из трёх значений типа 'int')
int c[10]; // Полный тип
Также на массивы неизвестного размера могут быть созданы ссылки и указатели, но в языке С++ они не могут быть инициализированы (или присвоены) указателями на массивы с известным размером. Данное ограничение отсутствует в языке С, т. к. там указатели на обычные массивы и на массивы неизвестного размера совместимы, а поэтому могут свободно конвертироваться и присваиваться в обоих направлениях.
extern int a[];
int (&a1)[] = a; // ОК
int (*a2)[] = &a; // ОК
int (*a3)[2] = &a; // Ошибка в С++, но корректно в С
int b[] = {1, 2, 3};
int (&b1)[] = b; // Ошибка
int (*b2)[] = &b; // Ошибка в С++, но корректно в С
0