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

void func(class SD) {

}

class SD {

};

Почему код выше не работает, я ведь указываю компилятору что тип SD - это пользовательский тип данных тем самым объявил его.

void func(class SD&) {

}

class SD {

};

Почему это работает тогда?


Ответы (1 шт):

Автор решения: maestro

В первом примере class SD - это так называемый неполный тип. Конструкция даёт компилятору знать, что это пользовательский тип, который определён полностью где-то в другом месте. И всё. На данном этапе компилятор не знает ни его размер, ни перечень конструкторов, членов класса. И поэтому не может сгенерировать никакой код с его участием.

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

Критерии неполного типа

Рассмотрим, какие типы можно назвать неполными.

  1. Тип void. При этом его невозможно сделать полным:
void a;   //Ошибка: это неполный тип.
void *b;  //Указатель - это полный тип. Но под этим указателем 
//может скрываться любой тип; при его разыменовывании нужно будет 
//предварительно преобразовать его в указатель на полный тип.
  1. Класс, который был объявлен, но не определён (например, с помощью предварительного объявления):
class A;   //Это неполный тип.
class B{}; //Это полный тип.
class A{}; //Здесь класс A дополнен. С этого момента он стал полным типом.
  1. Массив неизвестной длины:
extern int a[];     //это неполный тип.
extern int b[10];   //это полный тип.
  1. Массивы, элементы которых имеют неполный тип:
class A;
A a[10];   //это неполный тип.
  1. Перечисления, когда они были объявлены, но нижележащий тип ещё не определён.

Что нельзя делать с неполными типами?

  1. Определять и вызывать функции, которые принимают неполный тип как аргумент или возвращают его:
class A;
void f(A a){}  //ошибка: аргумент функции имеет неполный тип.
A f()        //Ошибка: функция возвращает неполный тип.
{
//Здесь и написать ничего не получится, так как объект неполного типа невозможно создать.
}

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

  1. Определять объект неполного типа (например, в теле функции):
class A;
A a1;        //Ошибка: попытка создания объекта неполного типа.
void f() {
    A a2;    //Ошибка: попытка создания объекта неполного типа.
    static A a3; //Ошибка: попытка создания объекта неполного типа.
}
  1. Определять нестатический член класса неполного типа:
class A;
class B {
    A a;   //Ошибка: в классе B определён член неполного типа.
};
  1. Выделять память для объекта неполного типа с помощью оператора new:
class A;
A *a = new A;  //Ошибка: попытка выделения памяти для неполного типа.
  1. lvalue-to-rvalue conversion applied to a glvalue of type T;
  2. Выполнять явное или неявное приведение типа к неполному типу;
  3. 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;
  4. Выполнять доступ к членам класса неполного типа1:
class A;
A::static_member = 0;  //Ошибка: попытка доступа к статическому члену неполного типа

A* f();
A *a = f();
a->member = 0;   //Ошибка: попытка доступа к нестатическому члену неполного типа
  1. Применять операторы sizeof, typeid, alignof к неполному типу:
class A;
size_t s = sizeof(A);  //Ошибка: попытка вычислить размер неполного типа.
  1. Наследовать классы от неполного типа:
class A;
class B : public A {}; //Ошибка: попытка унаследовать неполный тип.
  1. Выполнять присваивание неполного типа к lvalue:
class A;
A getA();    //Предположим, что эта функция имеет определение в другой единице трансляции, где тип A является полным
A a = getA();  //Но в этой единице трансляции тип A является неполным, поэтому присваивание запрещено.
  1. Использовать неполный тип, а также ссылку и указатель на него в конструкции try-catch:
class A;
void f();
try
{
    f();
}
catch (A*)  //Ошибка: попытка использования указателя на неполный тип в try-catch.
{    
}


1 На странице документации не указан отдельно запрет разыменовывания указателя на неполный тип, я объединил его c запретом на доступ к членам класса с помощью оператора ->.

→ Ссылка