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?
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 int
s 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.