Search code examples
c++oopstructconstructorinitializer-list

Advantages of aggregate classes over regular classes


In my use case I needed to initialize a class variable using an initializer list. I learnt that an aggregate class is a class that just has user defined data members in it.

The advantage of aggregate is that we can use initializer list like this

struct fileJobPair {
        int file;
        int job;
};

fileJobPair obj = {10, 20};

But if I add a constructor to it, the class no longer remains an aggregate

struct fileJobPair {
        int file;
        int job;
        fileJobPair() {
            file = job = 0;
        }
        fileJobPair(int a, int b) {
            file = a;
            job = b;
        }
};

But I see that the initializer list advantage that we had for aggregate classes can still be used over here.

fileJobPair obj = {10, 20};

So my question is why do we even need an aggregate if the same thing can be acieved by regular class. What are the advantages and real life use case of aggregates.


Solution

  • What are the advantages <...> of aggregates

    Unlike "usual" classes, aggregate types:

    1. have pre-defined "constructor" (your example)
    2. need no tuple-like interface boilerplate for structured bindings:
    struct { int field1, field2; } aggregate;
    auto&& [_1, _2] = aggregate;
    
    1. have designated initializers:
    Aggregate{.something = 42, .something_else = "whatever"};
    

    Maybe there's something else I didn't think about.

    What are the <...> real life use case of aggregates

    E.g. you can (de)serialize them with no boilerplate thanks to #2, see also Boost.PFR. You can easily merge them (like tuples), "foreach" their fields etc.

    An example for #3: replace tons of the Builder pattern code with

    struct Foo {
      struct Builder { std::string_view a, b, c; };
      constexpr Foo(Builder); // TODO
    } example{{.a = "cannot set a field twice", .c = "can skip fields"}};
    

    the same thing can be acieved by regular class

    As you can see, it either cannot or requires extra boilerplate.