Structs and Enumerations
A struct groups related values into one type; an enum class defines a type with a fixed set of named values. Together they let you model data directly — a Book with a title and a Genre — instead of juggling loose variables.
Structs
#include <iostream> #include <string> struct Book { std::string title; std::string author; int year = 0; // default member initialiser double price = 0.0; }; int main() { Book b{"The C++ Programming Language", "Bjarne Stroustrup", 2013, 59.99}; b.price = 49.99; std::cout << b.title << " by " << b.author << " (" << b.year << "), " << b.price << '\n'; Book draft; // uses the defaults draft.title = "Untitled"; std::cout << draft.title << ' ' << draft.year << '\n'; }
The C++ Programming Language by Bjarne Stroustrup (2013), 49.99 Untitled 0
Members are accessed with a dot. Default member initialisers (int year = 0;) make sure members are never left uninitialised.
Designated initialisers
C++20 lets you name the members you initialise — clearer when a struct has many of them. They must appear in declaration order:
#include <iostream> struct WindowOptions { int width = 800; int height = 600; bool fullscreen = false; bool resizable = true; }; int main() { WindowOptions opts{.width = 1280, .fullscreen = true}; std::cout << opts.width << 'x' << opts.height << ", fullscreen " << opts.fullscreen << ", resizable " << opts.resizable << '\n'; }
1280x600, fullscreen 1, resizable 1
Structured bindings
#include <iostream> #include <string> #include <vector> struct Score { std::string player; int points; }; int main() { std::vector<Score> table{{"Ana", 120}, {"Ben", 95}}; for (const auto& [player, points] : table) { std::cout << player << ": " << points << '\n'; } }
Ana: 120 Ben: 95
Comparing structs
C++20 can generate comparison operators for you. Defaulting == compares every member; defaulting <=> gives all six comparisons in member order:
#include <algorithm> #include <compare> #include <iostream> #include <vector> struct Version { int major = 0, minor = 0, patch = 0; auto operator<=>(const Version&) const = default; }; int main() { std::vector<Version> versions{{1, 10, 0}, {1, 2, 3}, {2, 0, 0}, {1, 2, 10}}; std::sort(versions.begin(), versions.end()); for (const auto& v : versions) std::cout << v.major << '.' << v.minor << '.' << v.patch << ' '; std::cout << '\n' << std::boolalpha << (Version{1, 2, 3} == Version{1, 2, 3}) << '\n'; }
1.2.3 1.2.10 1.10.0 2.0.0 true
enum class
A scoped enumeration defines its own type with named values. Its values must be written with the type name, and they don't silently turn into integers:
#include <iostream> #include <string_view> enum class Genre { fiction, science, history, poetry }; std::string_view to_string(Genre g) { switch (g) { case Genre::fiction: return "fiction"; case Genre::science: return "science"; case Genre::history: return "history"; case Genre::poetry: return "poetry"; } return "?"; } int main() { Genre g = Genre::science; std::cout << to_string(g) << '\n'; std::cout << static_cast<int>(g) << '\n'; // explicit conversion when you need the number }
science 1
An enum class can't be compared with an int or a value of another enum by accident:
enum class Colour { red, green }; enum class Fruit { apple, lime }; int main() { Colour c = Colour::red; if (c == Fruit::apple) return 1; // error: different types int n = c; // error: no implicit conversion return n; }
main.cpp:6:11: error: invalid operands to binary expression ('Colour' and 'Fruit')
main.cpp:7:9: error: cannot initialize a variable of type 'int' with an lvalue of type 'Colour'The older plain enum (without class) puts its names into the surrounding scope and converts to int freely — you'll see it in older code and C libraries. Prefer enum class. You can choose the underlying type: enum class Level : std::uint8_t { low, high };.
A struct with an enum
#include <iostream> #include <string> #include <vector> enum class Status { todo, doing, done }; struct Task { std::string title; Status status = Status::todo; }; int main() { std::vector<Task> board{{"Design", Status::done}, {"Build", Status::doing}, {"Test"}}; int open = 0; for (const auto& t : board) { if (t.status != Status::done) ++open; } std::cout << open << " of " << board.size() << " tasks still open\n"; }
2 of 3 tasks still open
Try it yourself
Model playing cards with a Suit enum class and a Card struct, and print a small hand.
1 of spades 10 of hearts 12 of clubs