Почему можно передать ссылку на объект пользовательского типа данных, но нельзя передать сам объект?
void func(class SD) {
}
class SD {
};
Почему код выше не работает, я ведь указываю компилятору что тип SD - это пользовательский тип данных тем самым объявил его.
void func(class SD&) {
}
class SD {
};
Почему это работает тогда?
Ответы (1 шт):
В первом примере class SD - это так называемый неполный тип. Конструкция даёт компилятору знать, что это пользовательский тип, который определён полностью где-то в другом месте. И всё. На данном этапе компилятор не знает ни его размер, ни перечень конструкторов, членов класса. И поэтому не может сгенерировать никакой код с его участием.
С указателями и ссылками на неполные типы можно выполнить некоторые операции, но их, в основном, используют для того, чтобы передать их в другую единицу трансляции, в которой этот тип является полным. Ссылки и указатели всегда являются полными типами, так как их размер известен всегда.
Критерии неполного типа
Рассмотрим, какие типы можно назвать неполными.
- Тип
void. При этом его невозможно сделать полным:
void a; //Ошибка: это неполный тип.
void *b; //Указатель - это полный тип. Но под этим указателем
//может скрываться любой тип; при его разыменовывании нужно будет
//предварительно преобразовать его в указатель на полный тип.
- Класс, который был объявлен, но не определён (например, с помощью предварительного объявления):
class A; //Это неполный тип.
class B{}; //Это полный тип.
class A{}; //Здесь класс A дополнен. С этого момента он стал полным типом.
- Массив неизвестной длины:
extern int a[]; //это неполный тип.
extern int b[10]; //это полный тип.
- Массивы, элементы которых имеют неполный тип:
class A;
A a[10]; //это неполный тип.
- Перечисления, когда они были объявлены, но нижележащий тип ещё не определён.
Что нельзя делать с неполными типами?
- Определять и вызывать функции, которые принимают неполный тип как аргумент или возвращают его:
class A;
void f(A a){} //ошибка: аргумент функции имеет неполный тип.
A f() //Ошибка: функция возвращает неполный тип.
{
//Здесь и написать ничего не получится, так как объект неполного типа невозможно создать.
}
Особым случаем здесь будут функции, возвращающие void. Это неполный тип, но возврат функцией типа void трактуется как отсутствие возвращаемого значения.
- Определять объект неполного типа (например, в теле функции):
class A;
A a1; //Ошибка: попытка создания объекта неполного типа.
void f() {
A a2; //Ошибка: попытка создания объекта неполного типа.
static A a3; //Ошибка: попытка создания объекта неполного типа.
}
- Определять нестатический член класса неполного типа:
class A;
class B {
A a; //Ошибка: в классе B определён член неполного типа.
};
- Выделять память для объекта неполного типа с помощью оператора
new:
class A;
A *a = new A; //Ошибка: попытка выделения памяти для неполного типа.
- lvalue-to-rvalue conversion applied to a glvalue of type T;
- Выполнять явное или неявное приведение типа к неполному типу;
- a standard conversion, dynamic_cast, or static_cast to type T* or T&, except when converting from the null pointer constant or from a pointer to possibly cv-qualified void;
- Выполнять доступ к членам класса неполного типа1:
class A;
A::static_member = 0; //Ошибка: попытка доступа к статическому члену неполного типа
A* f();
A *a = f();
a->member = 0; //Ошибка: попытка доступа к нестатическому члену неполного типа
- Применять операторы
sizeof,typeid,alignofк неполному типу:
class A;
size_t s = sizeof(A); //Ошибка: попытка вычислить размер неполного типа.
- Наследовать классы от неполного типа:
class A;
class B : public A {}; //Ошибка: попытка унаследовать неполный тип.
- Выполнять присваивание неполного типа к lvalue:
class A;
A getA(); //Предположим, что эта функция имеет определение в другой единице трансляции, где тип A является полным
A a = getA(); //Но в этой единице трансляции тип A является неполным, поэтому присваивание запрещено.
- Использовать неполный тип, а также ссылку и указатель на него в конструкции try-catch:
class A;
void f();
try
{
f();
}
catch (A*) //Ошибка: попытка использования указателя на неполный тип в try-catch.
{
}
1 На странице документации не указан отдельно запрет разыменовывания указателя на неполный тип, я объединил его c запретом на доступ к членам класса с помощью оператора ->.