9

In C++, an aggregate is (taken from 8.5.1p1 of the language spec)

an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

So, #1 is not an aggregate, but #2 is an aggregate. Why is #1 not an aggregate aswell?

struct A { virtual void bark() { } int a; }; // #1
struct B { A b; }; // #2
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 3
    "[..] and no virtual functions (10.3)" ? – MrDuk Apr 23 '14 at 15:15
  • Your cite "no virtual functions " tells us that it is an aggregate. – harper Apr 23 '14 at 15:15
  • @MrDuk Yes, I can verify that this is the last part of the text that I quoted. Or did you want to ask a different question? – Johannes Schaub - litb Apr 23 '14 at 15:15
  • The vtbl, maybe? By the way, I'm not sure a `struct` qualifies as `an array or a class` – Frédéric Hamidi Apr 23 '14 at 15:15
  • Please provide the paragraph your quote refers to. –  Apr 23 '14 at 15:15
  • @FrédéricHamidi the only difference in C++ between structs and classes is their default access modifiers and default inheritance type (public vs. private) – Marius Bancila Apr 23 '14 at 15:17
  • @Marius, so the standard routinely uses `class` to mean `struct or class`? – Frédéric Hamidi Apr 23 '14 at 15:18
  • 2
    @FrédéricHamidi it uses *class* to mean *class declared by `struct`, `class` or `union`* – Johannes Schaub - litb Apr 23 '14 at 15:19
  • The vtbl pointer of `B::b` needs to be set aswell, so I'm no sure why vtbls would rule out `A` but not `B`. – Johannes Schaub - litb Apr 23 '14 at 15:22
  • @Johannes, because `A` not being an aggregate does not impact the initialization of `B`. From [What are Aggregates and PODs and how/why are they special?](http://stackoverflow.com/q/4178175/464709), `If virtual functions are present, it means that the objects of this class have (on most implementations) a pointer to the so-called vtable of the class, which is set in the constructor, so brace-initialization would be insufficient.` I *think* `B` can be brace-initialized because an instance of `A` can be provided in the braced-init-list, and that instance would be assigned to the member `b`. – Frédéric Hamidi Apr 23 '14 at 15:35
  • @FrédéricHamidi *"I think B can be brace-initialized because an instance of A can be provided in the braced-init-list"* - well, you can also say `B b = { }`, which appears to render this argument invalid. Indeed, the data member is value initialized, and not aggregate initialized. But what matters here to me as a user is that the user aggregate initialized `B`, but can't aggregate initialize `A`. I don't see the need for this annoying difference yet. – Johannes Schaub - litb Apr 23 '14 at 15:55
  • Also, *"which is set in the constructor, so brace-initialization would be insufficient."* - why can't the constructor run after brace initialization has been carried out? In C++14 we can have in-class initializers in aggregates. Why don't do so for `virtuals`. – Johannes Schaub - litb Apr 23 '14 at 15:58
  • @litb: So what you are really doing is questioning the rationale for this definition and the "actual" question is purely rhetorical..? – ltjax Apr 23 '14 at 16:02
  • @ltjax no, my *actual* question is "what is the rationale?". I won't question the rationale if I'm given an explanation, but so I far have been given a link to an SO answer, which may point to the excellent opinion of another SO poster, but hardly explain rationale authoratively. – Johannes Schaub - litb Apr 23 '14 at 18:12
  • In C++03, aggregate-ness was prerequisite for POD-ness ([class]/4), hence no virtual functions. It could just be a leftover that no one realized is no longer needed. – Casey Apr 24 '14 at 17:50

2 Answers2

9

Why is #1 not an aggregate aswell?

Because the standard definition of aggregates says that it isn't. The definition says that the class cannot have virtual functions if it is to be considered an aggregate type. That's it. That's the "captain obvious" answer.

Since you quoted the standard definition (and read it, I presume), you know this already, so I must assume that what you are asking is whether there is any fundamental reason why a class with virtual functions cannot be an aggregate type.

Obviously, a class with virtual functions must have it's virtual table pointer (or other mechanism) initialized as part of the compiler-generated constructor. And the only thing that aggregates really provide (in addition to serving as a base definition for PODs and the like) is the ability to do a curly-braced initialization of its data members. I don't see any reason why the compiler could not simply permit the curly-braced initialization syntax while doing the initialization of the virtual table pointer too.

As far as B is concerned, it can be an aggregate because the brace-initialization is possible as long as there is a way to construct the data members from whatever is provided (or not provided) in the initialization list, i.e., it needs each data members to have some (compiler-generated or not) default, copy or move constructor. Remember, the definition of aggregates is shallow as opposed to recursive like the POD definitions (trivial, std-layout), meaning that only the top-level class has those restrictions, not its sub-objects.

The definition of aggregates is obviously very reminiscent of the C struct restrictions, and the brace-initialization for structs is obviously also a feature carried over from C. I believe that for historical reasons, the definition of aggregates was built such that it reflects C structs, and not for reasons of what is or isn't possible for the compiler to do.

I would certainly be of the opinion that there is no good reason for the restriction on aggregates to not have virtual functions. Maybe a proposal should be made to the standard committee to remove this restriction (as it wouldn't break any existing code). Especially now, with unified initialization syntax, aggregates are nothing more than classes where the compiler can generate a constructor with all the data members as parameters (with default values being default-constructed objects). The only other purpose for aggregates is to lump a few of the restrictions that apply to POD classes (trivial, standard-layout, etc.), for which the restriction on not having virtual functions is justified (AFAIK), but that is just a matter of moving that restriction over to PODs.

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
  • 1
    *Obviously, a class with virtual functions must have it's virtual table pointer initialized as part of the compiler-generated constructor.* => I would like to point out that the C++ Standard does not mandate using a virtual table, and thus a virtual table pointer. It is, indeed, a very common implementation of the `virtual` dispatch mechanism, and as such weighs on all decisions surrounding `virtual` handling, but is it *just* an implementation detail. – Matthieu M. Apr 23 '14 at 16:26
  • @MatthieuM. Sure, that's true. But the logic is the same regardless of the implementation detail. If the compiler can generate a default constructor (or a default constructor with in-class member initializers) for any class (incl. with virtual functions), then it should be able to generate a constructor to be used with the brace-initialization syntax for aggregates. I think the virtual function restriction is simply misplaced in the standard, it should be only for the POD definitions, not for aggregates. – Mikael Persson Apr 23 '14 at 16:34
  • 1
    POD-ness required aggregate-ness in C++03 (C++03 [class]/4), that is no longer the case. PODs are defined as recursively *trivial* and *standard-layout* (C++11 [class]/10), neither of which requires being an aggregate. It's possible for a class/union to be both a POD and an aggregate, but not required. Removing the restriction on virtual functions from aggregates would this not require any changes to the POD definitions. – Casey Apr 24 '14 at 17:46
  • @Casey I fail to see the significance wrt PODs here. They could just have forbidden virtual functions for PODs only, and leave aggregates powerful. – Johannes Schaub - litb Apr 24 '14 at 18:09
  • @JohannesSchaub-litb The last sentence in the answer: "The only other purpose for aggregates is to lump a few of the restrictions that apply to POD classes (trivial, standard-layout, etc.), for which the restriction on not having virtual functions is justified (AFAIK), but that is just a matter of moving that restriction over to PODs." only makes sense in the context of C++03, when the premise of the argument was true. It is not true since C++11. – Casey Apr 24 '14 at 18:19
  • @Casey Thanks for pointing out that aggregrates no longer factor into the POD definitions (trivial and std-layout), I just assumed they still did. I just did a word search through the standard for "aggregate", and it seems that besides a few sporadic mentions here and there (e.g., for "literal types"), the only real relevance of aggregates is for its special initialization syntax, which, like I said, doesn't justify the restriction on virtual functions. So, it looks more and more like it was there for historical reasons, and nobody bothered to revise it for C++11/14. – Mikael Persson Apr 25 '14 at 06:01
1

I can't say for sure what was in the committee's minds when they added that restriction to the definition of aggregates. I suspect that it's because they considered that requiring the compiler to also have to set up an instance for virtual function calls (vtable or other) would be sufficiently different from simply initializing values and so added the restriction to simplify the requirements on language implementors.

Mark B
  • 95,107
  • 10
  • 109
  • 188