Search code examples
c++c++11constructorclass-constructors

The compiler creates a constructor of N args when I have N public member variables and no private ones. What is happening here?


The following code compiles and runs as expected, unless I uncomment one of the two commented out lines that I have labelled with "prevents compilation":

#include <string>
#include <iostream>

class Animal {
    public:
    int count;
    std::string foobar;
    //Animal() = delete; // prevents compilation

    void squeak() const {
        std::cout << count << ". Squeak from " << foobar << std::endl;
    }
    private:
    //int priv; // prevents compilation
};

int main() {  
    std::string foo{"foo"};
    Animal mouse(1, foo);
    //Animal mouse{1, foo};
    //Animal mouse{1, foo, 3};
    mouse.squeak();
    std::cout << "\n";
}

What is happening regarding constructors here, what is this mechanism called and when should one use it?


Solution

  • This is known as "aggregate initialization".

    When your class is an "aggregate", brace-initialization can be used to populate your members. When it is not, it cannot.

    Aggregates are classes that are just a bundle of members. Like an aggregate rock, which is made by smooshing together a bunch of different materials.

    You are looking at two ways to prevent your class from being an aggregate. One of them is having a private member, and the other is by you explicitly having a constructor.

    struct foo {
      int x;
      int y;
    };
    

    can be initialized via

    foo f = {1,2};
    

    There are a bunch of relatively complex rules about when aggregate initialization occurs. But you ran into the two most common ones here.

    For the most part, when you disable aggregate initialization on a class by adding code, the effect is a build break not incorrect code. So it isn't a real huge pain point.

    I do strongly advise you to:

    int count = 0;
    std::string foobar;
    

    initialize ints like that at the point of declaration in a class. This won't block aggregate initialization, but when going from aggregate initialization to non-aggregate initialization variables can become uninitialized in some situations if you don't do this.