10

In C++11 a type specifier includes class specifiers and enum specifiers. (aka class definitions and enumeration definitions)

According to the grammar/syntax - type specifiers can appear in several places in the language, but not in all those places are class specifiers and enum specifiers allowed.

For example:

struct C{} c;
// ok: types may be defined in the specifiers of a simple declaration

void f(struct S{});
// error: types may not be defined in parameter types

constexpr auto i = sizeof(enum E{});
// error: types may not be defined in ‘sizeof’ expressions

Where in the standard does it partition these uses of type specifiers into those where types may and may not be defined? For example, where is the rule that says types may not be defined in a sizeof expression?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • Presumably for `sizeof` at least this can be inferred from the `Unary expressions` grammar specified in 5.3/1, but it's not obvious from a cursory look (due to recursive definitions). – Mark B Jan 07 '14 at 19:51
  • +1, nice question. But reading the Standard on this particular topic is rather disorienting, with references all over the place. BTW, can you show an example where you actually *need* this stuff? IOW, isn't it always possible to split the definitions into two parts? – TemplateRex Jan 07 '14 at 19:59
  • 1
    @MarkB: `sizeof(type-id)` -> `type-id` -> `type-specifer-seq` -> `type-specifier` -> `class-specifier`. Appendix A is a grammar summary by the way. Syntactically it is allowed, so there must be some text somewhere that says you can't define a type there. – Andrew Tomazos Jan 07 '14 at 20:01
  • 1
    ah, if only Clang 4.X would show the Standard references in compiler errors ;-) – TemplateRex Jan 07 '14 at 20:05
  • 1
    @TemplateRex: That's not a bad idea actually. Quite some work to do the matching though. – Andrew Tomazos Jan 07 '14 at 20:06
  • [CWG 686](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#686) might be interesting. – dyp Jan 22 '14 at 18:21

2 Answers2

8

The reason it can't be found in the C++ standard is because it's actually prohibited in a delta from the C standard.

In C.1.4 we have the following: Change: Types must be declared in declarations, not in expressions In C, a sizeof expression or cast expression may create a new type. which shows the prohibition in question.

This is explicitly called out in 7.1.6/3:

At least one type-specifier that is not a cv-qualifier is required in a declaration unless it declares a constructor, destructor or conversion function.92 A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (7.1.3) that is not the declaration of a template-declaration.

where the part of particular interest is that A type-specifier-seq shall not define a class or enumeration unless...

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • 1
    Sadly that section is `informative` not `normative` -- it is intended to tell you what the rest of the document says, it should not define new information. I tried and failed to find the `normative` text for it. – Yakk - Adam Nevraumont Jan 07 '14 at 20:44
  • @hvd: No it's a decl-specifer-seq, a decl-specifier-seq is never a type-specifier-seq, although a decl-specifier is sometimes a type-specifier. – Andrew Tomazos Jan 07 '14 at 21:39
  • @AndrewTomazos Ah, thanks, it's a type-specifier (a decl-specifier can be a type-specifier) but not a type-specifier-seq. –  Jan 07 '14 at 21:40
  • 2
    Interestingly, this seems to allow type definitions in a *for-range-declaration*: `for (struct S2 {} s2: s) { }`, which gets accepted by GCC with `-std=c++11 -pedantic` without any diagnostic, but for which clang gives an explicit "error: types may not be defined in a for range declaration" –  Jan 07 '14 at 21:45
  • @hvd: Unless there is some additional text, Clang is wrong, it's a decl-specifier-seq and neither a return type or parameter type. – Andrew Tomazos Jan 07 '14 at 21:53
  • @AndrewTomazos Similarly for a *condition*? `if (struct S2 {} s2 = s1) {}` is rejected by both compilers, though (even if I make `S2` convertible to `bool`), so I wouldn't be surprised if there is some normative text somewhere that I can't find. –  Jan 07 '14 at 21:53
  • @hvd: Yes that condition is a simple-declaration and contains a decl-specifier-seq, and is neither a parameter type, nor a return type. – Andrew Tomazos Jan 07 '14 at 21:54
  • @hvd: I hereby volunteer you. :) – Andrew Tomazos Jan 07 '14 at 22:03
  • @AndrewTomazos I was mistaken about the intent of the "(either introduced by the type-specifier-seq or the declarator of the condition)" bit in [stmt.select]p3. That has been there for longer and applies to classes *declared* in conditions. `if (struct S *p = 0) { S *s = 0; }` was valid before C++11. And I now think, in fact, that the intent is probably *not* to allow class definitions, because http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#948 (the issue that resulted in the change) doesn't mention allowing this at all. Either way, GCC bug reported, let's see what they say. –  Jan 08 '14 at 18:54
  • @hvd: I think you are right that DR 948 changing type-specifier-seq to decl-specifier-seq caused the unintended side-effect of newly allowing class-specifiers there and killing 7.1.6/3. I'll follow up with the standards commitee. – Andrew Tomazos Jan 08 '14 at 19:45
  • @hvd: Ok I reported it to the commitee here: https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-discussion/KRQXGrjDSzI – Andrew Tomazos Jan 08 '14 at 19:56
  • @AndrewTomazos Thanks. Checking the active issues, I managed to find it, it's http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1732. I've added a comment to the GCC bug too. –  Jan 08 '14 at 20:59
3

From N3797:

8.3.5/9 Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted ( 8.4.3 ) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).

This blocks defining new types in a function declaration.

The next two are other corner cases not mentioned by the OP:

11.3/2 A class shall not be defined in a friend declaration.

14.1/2 Types shall not be defined in a template-parameter declaration.

Finally, this clause blocks it in sizeof and elsewhere:

7.1.6/3 A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration (7.1.3) that is not the declaration of a template-declaration

Note that C does not have that restriction (C.1.4)

In addition, in a previous version of the C++ standard, we had:

5.3.3p5 Types shall not be defined in a sizeof expression

which I cannot find in the most recent version standard proposals, and it is redundant under N3797 as sizeofs route to define a type in the grammar is via type-specifier-seq, and blocked by 7.1.6/3:

sizeof(type-id) -> type-id -> type-specifer-seq -> type-specifier -> class-specifier

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 2
    `sizeof` is always an expression so the `Types shall not be defined in a sizeof expression` would have been redundant to `Types must be declared in declarations, not in expressions.` – Mark B Jan 07 '14 at 20:43