Почему class {int i; }; не является полностью стандартно-совместимым?
Это следующий вопрос.
В предыдущем вопросе @JohannesSchaub-litb сказал, что следующий код не полностью стандартно-совместимый:
class { int i; }; //unnamed-class definition. § 9/1 allows this!
а затем добавил:
в то время как он является грамматически корректным, он нарушает правило, согласно которому такой класс должен объявить хотя бы одно имя в своей охватывающей области.
Я не мог этого понять. С каким именем он говорит?
Может ли кто-нибудь более подробно остановиться на этом (желательно со ссылкой на стандарт)?
Ответы
Ответ 1
В разделе 9 стандарта допускается class {public: int i;}
(обратите внимание на отсутствие конечной точки с запятой), потому что этот decl-specifier-seq для неназванного класса может использоваться в какой-либо другой конструкции, такой как typedef или объявление переменной. Проблема с class {public: int i;};
(обратите внимание, что последняя точка с запятой теперь присутствует) заключается в том, что эта спецификация класса теперь становится объявлением. Это незаконное объявление в соответствии с пунктом 7 статьи 7 стандарта:
В таких случаях и за исключением объявления неназванного битового поля (9.6), decl-specifier-seq должен ввести одно или несколько имен в программу или обновить имя, введенное предыдущим объявлением.
Ответ 2
Дело в том, что, объявив class{ int i; };
, вы собираете кучу символа (int i
, в этом случае), вы не сможете использовать нигде в любом коде.
Чтобы этот код имел смысл, вы должны хотя бы сделать одно из следующих действий:
class Myclass { int i; }; //I can furthermore instantiate variables of Myclass
class { int i; } myvar; //This in fact creates a myvar object
typedef class { int i; } MyType; //I can funthermore instantiate variables of MyType
Говоря просто class{ int i; };
, вы говорите компилятору:
- сохраните
int
и назовите его i
,
- оберните его в
class
Я никогда не позвоню и...
- Забудь об этом! (
};
)
Если вы удалите это объявление из своей программы, ничего не изменится.
Ответ 3
class { int i; };
не является допустимым объявлением, потому что это простая декларация без списка init-declarator, но она не вводит (или повторно объявляет) имя класса.
ISO/IEC 14882: 2011 7 [dcl.dcl]/3:
В простом объявлении необязательный список init-declarator может быть опущен только при объявлении класса (раздел 9) или перечисления (7.2), то есть когда spec-specifier-seq содержит либо спецификатор класса, специфицированный спецификатор типа с ключом класса (9.1) или спецификатор перечисления. В этих случаях и всякий раз, когда спецификатор класса или спецификатор перечисления присутствует в описании-спецификаторе-seq, идентификаторы в этих спецификаторах относятся к числу имен, объявляемых объявлением (в качестве имен классов, перечисляемых имен или перечислений, в зависимости от синтаксиса). В таких случаях и за исключением объявления неназванного битового поля (9.6), decl-specifier-seq должен ввести одно или несколько имен в программу или обновить имя, введенное предыдущим объявлением.
Ответ 4
Сообщение об ошибке из GCC объясняет это довольно кратко:
$ cat > a.cc
class { int i; };
$ g++ -Wall -std=c++98 a.cc
a.cc:1: error: abstract declarator ‘<anonymous class>’ used as declaration
class { int i; }
является абстрактным декларатором (стандарт, §8), но не является допустимым объявлением (§7). Это правило, на которое ссылается @JohannesSchaub-litb: для действительного объявления вам нужно что-то объявить, например. имя класса или имя переменной.
Ответ 5
Вы нарушаете [basic.scope.pdecl]/6
, в котором говорится:
Точка объявления класса, сначала объявленного в специфицированном спецификаторе типа, выглядит следующим образом:
- для объявления формы
class-key attribute-specifier-seqopt identifier ;
идентификатор объявляется как имя класса в области, содержащей декларацию, иначе
- для уточненного типа-спецификатора формы
class-key identifier
если специфицированный спецификатор типа используется в предложении decl-specifier-seq или параметре-объявления функция, определенная в области пространства имен, идентификатор объявляется как имя класса в пространстве имен, содержит декларацию; в противном случае, кроме как объявление друга, идентификатор объявляется в наименьшее пространство имен или область блока, содержащую декларацию. [Примечание. Эти правила также применяются в пределах шаблоны. - end note] [Примечание. Другие формы специфицированного спецификатора типа не объявляют новое имя, и поэтому должен ссылаться на существующее имя типа. См. 3.4.4 и 7.1.6.3. - конечная нота]
- вы не создаете переменную анонимного типа
- вы не создаете тип
В стандарте есть еще один пример (в [basic.def]/2
), который доказывает, что ваш пример не соответствует стандарту:
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
В вашем примере ничего не определено (кроме анонимной структуры, к каким полям нельзя получить доступ).
Обратите внимание на исключение из перечисления, потому что этот случай вводит два значения для использования.