2017-01-04 9 views
47

Сниппет ниже компилирует (demo):Первый фрагмент ниже компилируется, а второй - нет. Зачем?

struct A{ int i = 10; }; 

int main() { 
    struct A{ int i = 20; }; 
    struct A; 
    struct A a; 
} 

Но это не делает:

struct A{ int i = 10; }; 

int main() { 
// struct A{ int i = 20; }; 
    struct A; 
    struct A a; 
} 

Я могу видеть, что ответ, вероятно, даваемое этими пунктами в стандарте:

[basic.lookup.elab]/2 и [basic.scope.pdecl]/7.

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

Обратите внимание, что в первом примере struct A является не первым объявлен в разработан типа-спецификаторstruct A;, но в определении struct A в main().

Во втором примере, struct A также не первый объявленный в разработан типа спецификаторstruct A;, но в определении struct A в глобальном масштабе.

+0

Почему был удален тег 'language-lawyer'? – giusti

+0

@giusti Я только что вставлял тег –

+1

Хотелось бы понять, почему @RyanHaining удалил его в первую очередь. Возможно, у него была причина. – giusti

ответ

67

Каждый из примеров содержит объявления двух различных классов, как с именем A.

Давайте различать между классами, переименовав один из них B:

struct A{ int i = 10; }; 

int main() { 
    struct B{ int i = 20; }; 
    struct B; 
    struct B b; 
} 

выше, является семантический идентичен первым примером. Класс A никогда не используется.

struct A{ int i = 10; }; 

int main() { 
    struct B; 
    struct B b; 
} 

Это семантически идентично вашему второму примеру. Вы пытаетесь создать объект неполного типа, объявленный вперед класс B.

переименование B обратно A ничего не меняет, потому что тогда декларация A в main тени Декларации другого A в глобальном масштабе.

[основной.lookup.Elab]/2

Если разработан тип спецификатор не имеет вложенного-имени-спецификатора и [...] если разработан для типа спецификатор появляется в объявлении с форма:

class-keyattribute-specifier-seq неавтоматическогоidentifier;

в разработан тип спецификатор является свидетельством того, что вводит классовое имя, как описано в [basic.scope.pdecl].

Так struct A; является декларация, что вводит имя класса в рамках декларации. Ни при каких обстоятельствах он не может ссылаться на класс, объявленный во внешней области.

[basic.scope.pdecl]/7

[Примечание: Другие формы разработаны типа-спецификатор не объявить новое имя [...] - конец отметить]

Подразумевается, эта форма разработана типа-спецификатор объявляет новое имя.

+0

1) [basic.scope.pdecl]/7 начинается со следующего оператора: 'Точка декларации класса ** сначала **, объявленная в спецификаторе уточненного типа, выглядит следующим образом:'. Это означает, что точки маркера (7.1) и (7.2) рассматриваются только в том случае, если декларация 'class A;' является ** первой **, объявленной в спецификаторе _alaborated-type-specifier_, что не так в обоих примерах, поскольку я указал в конце моего вопроса. –

+0

2) Итак, что касается [basic.scope.pdecl]/7, я не думаю, что мы можем сказать, что объявление 'struct A;' в main() в моем втором примере не может ссылаться на класс, объявленный во внешней области, как вы сказали выше. Спасибо за ваш ответ. –

+0

Я объяснил, почему во втором примере 'struct A;' ** является ** первым объявлением 'A', потому что' A' в глобальной области - это совершенно другой класс с тем же именем. – Oktalist

44

Во втором примере строка struct A; является декларацией о переходе для структуры A в области основной функции. Эта структура будет предпочтительнее глобального struct A. Следующая строка определяет переменную, которая называется a типа struct A. Так как struct A был объявлен в области основной функции, вот где компилятор будет искать его определение там. Он не находит его (он прокомментирован). Первый пример компилируется, потому что есть определение в той же области. Следующий пример компилируется, однако, поскольку он указал, что A находится в глобальном пространстве имен:

struct A{ int i = 10; }; 

int main() { 
// struct A{ int i = 20; }; 
    struct A; 
    struct ::A a; 
} 
+4

Я бы прямо сказал, что это объявление _forward_. – skypjack

+0

@skypjack спасибо. Я исправлю надзор. –

+2

@ FrançoisAndrieux: Я бы не характеризовал его как «надзор». Ваша оригинальная версия была технически правильной ('struct A;' является объявлением, но не определением). Однако писать «форвардную декларацию», а не «декларацию», делает ее легче читать для неязыковых юристов. –

5

Это не компилируется, потому что он не может найти определение А.

int main() { 
// struct A{ int i = 20; }; 
     struct A; 
     struct A a; 
} 

Код выше равно первый пример, как глобальные А омрачен местным А. В второй пример А не имеет определения. Это просто прототип. Предполагается, что прототипы помещаются перед фрагментом кода, которому требуется определение, когда определение помещается ПОСЛЕ кода, который ему нужен. Если компилятор не может найти это определение, он потерпит неудачу, потому что он не знает, что такое A (глобальное определение скрывается локальным прототипом, что приводит к его игнорированию).

+0

Продвижение без комментариев? Что случилось с этим ответом? – exilit

+0

@exilit Что вы имеете в виду? – theo2003

+0

Я имею в виду, что кто-то отклонил ваш ответ, но не оставил комментария, объяснив, почему он это сделал. – exilit