C++ course menu
C++ Structs and Enums: Grouping Data and Naming Choices
Learn C++ structs to group related values into one type, aggregate initialization, vectors of structs, passing structs to functions, structured bindings, and enums: plain enum vs enum class, underlying values and switching on enums.
On this page
So far each variable has held one value. Real data comes in groups: a student has a name, an age and marks; a point has an x and a y. A struct bundles related values into one new type. An enum gives names to a fixed set of choices, such as the days of the week or the status of an order.
Defining a struct#
#include <iostream>
#include <string>
struct Student {
std::string name;
int age = 0;
double marks = 0.0;
};
int main() {
Student s;
s.name = "Asha";
s.age = 20;
s.marks = 91.5;
std::cout << s.name << " (" << s.age << ") scored " << s.marks << '\n';
}Asha (20) scored 91.5struct Student { ... };defines a new type. Note the semicolon after the closing brace.- Each variable inside is a member (or field). Access members with the dot:
s.name. int age = 0;gives a default value, used when you do not set the member.
Initializing a struct#
Instead of setting members one by one, list the values in braces, in the order the members are declared:
#include <iostream>
#include <string>
struct Point {
double x = 0;
double y = 0;
};
struct Product {
std::string name;
double price = 0;
int stock = 0;
};
int main() {
Point origin; // defaults: 0, 0
Point p{3.5, -2};
Product pen{"Pen", 25.0, 120};
Product book{.name = "Notebook", .price = 60}; // C++20 designated initializers
std::cout << origin.x << ',' << origin.y << " " << p.x << ',' << p.y << '\n';
std::cout << pen.name << ' ' << pen.stock << " " << book.name << ' ' << book.stock << '\n';
}0,0 3.5,-2
Pen 120 Notebook 0C++20 designated initializers (.name = ...) name each member, which is clearer for structs with many fields. They must still appear in declaration order, and members you skip keep their defaults.
Vectors of structs#
Structs become powerful when you store many of them:
#include <iostream>
#include <string>
#include <vector>
struct Product {
std::string name;
double price = 0;
int stock = 0;
};
int main() {
std::vector<Product> inventory = {
{"Laptop", 55000, 12},
{"Headphones", 2500, 0},
{"Keyboard", 1800, 25},
};
double value = 0;
for (const Product& p : inventory) {
if (p.stock == 0) {
std::cout << p.name << " is out of stock\n";
}
value += p.price * p.stock;
}
std::cout << "Stock value: " << value << '\n';
}Headphones is out of stock
Stock value: 705000Structs and functions#
Pass structs by const reference to read them, and return them by value:
#include <cmath>
#include <iostream>
struct Point {
double x = 0;
double y = 0;
};
double distance(const Point& a, const Point& b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return std::sqrt(dx * dx + dy * dy);
}
Point midpoint(const Point& a, const Point& b) {
return {(a.x + b.x) / 2, (a.y + b.y) / 2};
}
int main() {
Point a{0, 0};
Point b{6, 8};
std::cout << distance(a, b) << '\n';
auto [mx, my] = midpoint(a, b);
std::cout << mx << ' ' << my << '\n';
}10
3 4auto [mx, my] = ... is a structured binding: it unpacks the struct's members, in order, into new variables.
Comparing structs#
By default, == does not work on your own structs. C++20 can generate the comparison for you:
#include <iostream>
struct Version {
int major = 0;
int minor = 0;
bool operator==(const Version&) const = default;
auto operator<=>(const Version&) const = default;
};
int main() {
Version a{2, 1};
Version b{2, 3};
std::cout << std::boolalpha << (a == b) << ' ' << (a < b) << '\n';
}false trueThe defaulted <=> ("spaceship") operator compares members in order, giving you <, >, <= and >=. You will learn how operators work in Operator Overloading.
struct vs class#
In C++, a struct and a class are almost identical; the only difference is that struct members are public by default and class members are private by default. By convention, use struct for simple bundles of data with no rules to protect, and class when the type has behaviour and invariants to maintain. See Classes and Objects.
Enums: naming a set of choices#
An enumeration defines a type whose values are a fixed list of names. Modern C++ uses enum class (a "scoped enum"):
#include <iostream>
enum class OrderStatus { Pending, Shipped, Delivered, Cancelled };
const char* to_text(OrderStatus s) {
switch (s) {
case OrderStatus::Pending: return "Pending";
case OrderStatus::Shipped: return "Shipped";
case OrderStatus::Delivered: return "Delivered";
case OrderStatus::Cancelled: return "Cancelled";
}
return "Unknown";
}
int main() {
OrderStatus status = OrderStatus::Shipped;
if (status == OrderStatus::Shipped) {
std::cout << "On its way!\n";
}
std::cout << to_text(status) << '\n';
}On its way!
ShippedUsing OrderStatus::Shipped instead of a number like 1 makes the code self-explanatory, and the compiler stops you from assigning an invalid value.
Underlying values#
Each enumerator has an integer value, starting from 0 unless you choose:
#include <iostream>
enum class Priority { Low = 1, Medium = 5, High = 10 };
int main() {
Priority p = Priority::High;
std::cout << static_cast<int>(p) << '\n';
}10An enum class does not convert to int automatically; you need static_cast. That strictness prevents mistakes like adding a priority to a day of the week.
Plain enum vs enum class#
Older code uses plain enum, whose names spill into the surrounding scope and convert silently to int:
enum Color { Red, Green, Blue }; // plain enum: 'Red' is a global name
enum class Fruit { Apple, Banana }; // scoped: write Fruit::AppleWith plain enums, two enums cannot both have a Red, and Red + 1 compiles without complaint. Prefer enum class in new code.
Common mistakes#
Practice#
Practice exercise
Define enum class Level { Beginner, Intermediate, Advanced } and struct Course { std::string title; int lessons; Level level; }. Store three courses in a vector, and print the titles of the courses that are not Beginner, plus the total number of lessons.
Show solution
#include <iostream>
#include <string>
#include <vector>
enum class Level { Beginner, Intermediate, Advanced };
struct Course {
std::string title;
int lessons = 0;
Level level = Level::Beginner;
};
int main() {
std::vector<Course> courses = {
{"C++ Basics", 17, Level::Beginner},
{"Pointers and Memory", 4, Level::Intermediate},
{"Templates", 7, Level::Advanced},
};
int total = 0;
for (const Course& c : courses) {
total += c.lessons;
if (c.level != Level::Beginner) {
std::cout << c.title << '\n';
}
}
std::cout << "Total lessons: " << total << '\n';
}Pointers and Memory
Templates
Total lessons: 28Key takeaway#
Finished this lesson?