A struct (or structure) is a program-defined datatype which can hold different datatype values called as data members. This is also an aggregate datatype.

A simple example of a struct can be,

struct Pixel
{
    int x{};
    int y{};
};
 
 Pixel a{};

Pixel is a struct containing x and y data members, which define the pixel position. Pixel is value-initialized so x and y values are most probably zero.

Accessing data members

We can access data members of a struct using dot (.) operator also called member selection operator. For example,

std::cout << a.x << "\n";
std::cout << a.y << "\n";

Initializing a struct

A struct can be initialized by providing an initializer list which is a braced list comma separated values. As struct is an aggregate, this is called aggregate initialization.

The data members are initialized from the values in the list in the order as they are defined in struct. For example,

Pixel a{1, 9};

If the initializer list is missing some values then data members either initialized with the defined default value or value-initialized (with 0 mostly). For example,

Pixel a{1};
 
std::cout << a.x << "\n"; // prints 1
std::cout << a.y << "\n"; // prints 0

Tip

Which means that we can initialize a struct simply as

Pixel a{};

It value-initializes all the data members.

Note

Consider the following case:

struct Pixel
{
   int x: // default initialize (bad. any garbage value)
   int y{}; // value initialize (default 0)
}

If we do Pixel a;, a.x is default initialized with a garbage value and a.y is value initialized. But if we do Pixel a{};, both are value initialized. So, we should follow latter approach.

Designated initializers

In the initializers list, it is also possible to provide data members name with the initialization value (using list or copy initialization). However, the order of the data members should be followed. For example,

Point point{.x{1}, .z{2}};
 
std::cout << point.x << "\n"; // 1
std::cout << point.y << "\n"; // 0
std::cout << point.z << "\n"; // 2

point.y is value initialized to zero.

Advantage with this method is that it makes it explicit which data member is initialized with what. Also, if we add any more data members in between, the initialization would not break as shown below,

struct Pixel
{
    int x;
    int test;
    int y;
};
 
Pixel a{1, 2};

We would want a.y to be 2 but it gets value initialized as 2 is initialized to test.

The problem with this approach is that it makes the initialization cluttered and hard to read.

Assignment with initializers list

If we have to change the value of data members of struct, we can individually do that. But if there is a requirement of changing whole, we can use initializers list to do so as shown below,

Pixel a{1, 2};
a = {2, 3};

Assignment with designated initializers

We can use designated initializers for assignment as shown below,

point = {.x{1}, .y{2}};
std::cout << point.x << "\n";
std::cout << point.y << "\n";
std::cout << point.z << "\n";

where point.z is value initialized to zero.

Initializing struct with same type struct

We can use a struct to initialize another same type struct as shown below,

Pixel b{a};
std::cout << b.x << "\n";
std::cout << b.y << "\n";

References

  1. https://www.learncpp.com/cpp-tutorial/struct-aggregate-initialization/