Следующее эквивалентно предварительному объявлению?

Это связано с недавним вопросом.

В основном следующий код:

class A
{
    class B* b;
    B* c;
};

компилируется, хотя class B не объявляется и не объявляется вперед. Эквивалентен ли этот синтаксис прямому объявлению? Есть ли отличия?


person Luchian Grigore    schedule 05.02.2012    source источник
comment
Я не понимаю, чем это принципиально отличается от вопроса, на который вы указали: Указатель на структуру, которая не была объявлена ​​   -  person CB Bailey    schedule 05.02.2012
comment
@CharlesBailey: Ну, это вопрос C ++, а не C, и C ++, как правило, более суетливо относится к требованию предварительных объявлений (например, фрагмент кода в связанном вопросе не будет компилироваться на C ++).   -  person j_random_hacker    schedule 05.02.2012
comment
@j_random_hacker: Хороший указатель, не заметил C в связанном вопросе, но почему связанный вопрос не компилируется как C ++? Мне и g++ -std=c++98 -pedantic это нравится.   -  person CB Bailey    schedule 05.02.2012
comment
Да, связанный вопрос действителен на C ++. Отличие C ++ заключается в том, что, поскольку классы не имеют общего пространства имен, правила определения пространства имен, на которое ссылается объявление класса, могут быть немного сложными.   -  person    schedule 05.02.2012
comment
Вы правы, @CharlesBailey, связанный вопрос действительно хорош, C ++! Виноват. Моя новая интерпретация разницы между этими вопросами: этот вопрос (в отличие от связанного вопроса) спрашивает о том, имеет ли предварительное объявление постоянный эффект (а именно, позволяет будущим определениям, включающим B, работать без написания class), когда он принимает форму определения, как здесь.   -  person j_random_hacker    schedule 05.02.2012


Ответы (3)


Вы можете объявить тип и объект в одном объявлении.

class B* b;

Объявляет тип B и объект b, который имеет указатель типа на B. Тип является неполным и ищется в той области, в которой он встречается, если поиск не может найти существующее объявление для класса, тогда тип называет тип в ближайшей охватывающей области пространства имен (строго некласс не-функция-прототип область, которая обычно является пространством имен). Объект является членом области видимости, в которой появляется объявление (в данном случае class A).

В большинстве случаев чаще объявляют полный тип и объект вместе, в этом случае тип иногда остается анонимным. Например.

struct { int a; int b; } x;

Соответствующие части стандарта для правил области видимости имен: 7.1.5.3 [dcl.type.elab] Подробные спецификаторы типа / 2 и упомянутые разделы в 3.4.4 и 3.3.1:

3.4.4 описывает, как выполняется поиск имени для идентификатора в спецификаторе детализированного типа. Если идентификатор преобразуется в имя-класса или имя-перечисления, спецификатор-разработанного-типа представляет его в объявление так же, как спецификатор простого типа вводит свое имя-типа. Если идентификатор преобразуется в typedef-name или шаблон type-parameter, расширенный-описатель-тип плохо сформирован. [...] Если поиск имени не находит объявления для имени, спецификатор детализированного типа неправильно сформирован, если он не имеет простой формы идентификатор ключа класса , и в этом случае идентификатор объявляется, как описано в 3.3.1.

person CB Bailey    schedule 05.02.2012
comment
Я считаю, что это немного неточно: B не является членом A, но относится к ::B и входит в сферу действия даже за пределами определения A. - person ; 05.02.2012
comment
Хотя, если другой B уже был в области видимости, например объявление, состоящее не более чем из class B;, тогда оно могло относиться к вложенному классу A::B. - person ; 05.02.2012
comment
На самом деле это ближайшая неклассовая область, не являющаяся прототипом функции, что редко имеет значение, но учтите void f() { struct s { struct p *ptr; } s; struct p { } p; s.ptr = &p; } :) - person ; 05.02.2012

Нет, это объявление указателя на B. Здесь вы не объявляете B, а только указатель на него, и в этом нет ничего особенного.

Изменить: я ошибся, извините. Смотрите другой ответ.

person Mr Lister    schedule 05.02.2012
comment
Если это не объявляет B, как объяснить отсутствие ошибки в строке 3, где ключевое слово class опущено? - person ; 05.02.2012
comment
@hvd хороший ответ, именно поэтому я объявил участника c. - person Luchian Grigore; 05.02.2012
comment
Так жаль. Но теперь я вижу, что кто-то поддержал мой ответ, даже если он был явно неправильным. Кто это сделал? - person Mr Lister; 05.02.2012

Я хотел бы добавить несколько деталей к ответу Чарльза Бейли:

class A
{
public:
    class B * b;
    class C {} *c;
    int d;
} a;

B* globalB;
// C* globalC;  identifier "C" is undefined here

int main(int argc, char *argv[])
{
    a.d = 1;
    cout << a.d;
}

Да, он сразу определяет неполный тип B и b как указатель на B. Но вот и самое интересное:
"Исключением для видимости области действия объявления вложенного класса является то, когда имя типа объявляется вместе с предварительным объявлением. В этом случае имя класса, объявленное предварительным объявлением, является видимый за пределами включающего класса, с его областью действия, определенной как наименьшая охватывающая неклассовая область. " (Объявления вложенных классов)

Это означает, что тип B определен вне области A, что позволяет вам определять переменную globalB. Если вы не хотите, чтобы B определялся вне области A, вы можете использовать {} точно так же, как он используется с типом C в моем примере.

Между прочим, этот пример правильный, и его результат: 1

person LihO    schedule 05.02.2012
comment
Я действительно проверил то же самое после того, как задал вопрос. - person Luchian Grigore; 05.02.2012