Подтвердить что ты не робот

Почему class {int i; }; не является полностью стандартно-совместимым?

Это следующий вопрос.

В предыдущем вопросе @JohannesSchaub-litb сказал, что следующий код не полностью стандартно-совместимый:

class { int i; };  //unnamed-class definition. § 9/1 allows this!

а затем добавил:

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

Я не мог этого понять. С каким именем он говорит?

Может ли кто-нибудь более подробно остановиться на этом (желательно со ссылкой на стандарт)?

4b9b3361

Ответ 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

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

Обратите внимание на исключение из перечисления, потому что этот случай вводит два значения для использования.