listings-Chap12-README-onepage.md
Listings of Chap12.docx
This is the list of listings on one page. You can also view a linked summary.
Listing 12.1: Creating your own data type with “struct”.
Book listing lst-0001-book.cpp:
// https://godbolt.org/z/8YqoTEx58
#include <string>
#include <iostream> // cout
#include <format>
using std::string; using std::cout; using std::format;
struct Person { // defines the new type Person
string name_;
int age_;
string city_;
}; // closing semicolon
void print(Person p) { // entire Person as one parameter
cout << format("{} ({}) from {}\n",
p.name_, p.age_, p.city_); // access via dot
}
int main() {
Person john {"John", 45, "Boston" }; // initialization
print(john); // call as a unit
}
Godbolt Listing lst-0001-godb.cpp, https://godbolt.org/z/8YqoTEx58:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/8YqoTEx58
#include <string>
#include <iostream> // cout
#include <format>
using std::string; using std::cout; using std::format;
struct Person { // defines the new type Person
string name_;
int age_;
string city_;
}; // closing semicolon
void print(Person p) { // entire Person as one parameter
cout << format("{} ({}) from {}\n",
p.name_, p.age_, p.city_); // access via dot
}
int main() {
Person john {"John", 45, "Boston" }; // initialization
print(john); // call as a unit
}
Listing 12.2: The declaration of a class initially only mentions its name, while the definition contains all its elements.
Book listing lst-0002-book.cpp:
// https://godbolt.org/z/zPbK16zWE
#include <memory> // shared_ptr
#include <vector> // vector
struct Employee; // class declaration
struct Boss; // class declaration
struct Employee { // class definition
std::shared_ptr<Boss> boss_; // pointer to Boss
void print() const; // method declaration
};
struct Boss { // definition
std::vector<std::shared_ptr<Employee>> employees_; // Pointer to Employees
void print() const; // method declaration
};
void Employee::print() const { // method definition
// …
}
void Boss::print() const { // method definition
// …
}
Godbolt Listing lst-0002-godb.cpp, https://godbolt.org/z/zPbK16zWE:
//#(compile) c++; compiler:g141; options:; libs:-
// https://godbolt.org/z/zPbK16zWE
#include <memory> // shared_ptr
#include <vector> // vector
struct Employee; // class declaration
struct Boss; // class declaration
struct Employee { // class definition
std::shared_ptr<Boss> boss_; // pointer to Boss
void print() const; // method declaration
};
struct Boss { // definition
std::vector<std::shared_ptr<Employee>> employees_; // Pointer to Employees
void print() const; // method declaration
};
void Employee::print() const { // method definition
// …
}
void Boss::print() const { // method definition
// …
}
Listing 12.3: With designated initializers, you specify the elements to be set.
Book listing lst-0005-book.cpp:
// https://godbolt.org/z/K8WePajvM
Person john1 {"John", 45, "Boston" }; // correct
Person john2 {"Boston", 45, "John" }; // oops, swapped, and no one notices
Person jack { .name_ = "Jack", .age_ = 23, .city_ = "Dallas" }; // okay
Person jimmi { .name_ = "Jimmi", .age_ = 48 }; // okay, not all specified
Person carl { "Carl", .age_ = 53 }; // (ERR) all designated or none
Person paul { .age = 34, .name_ = "Paul", .city = "Reno" }; // (ERR) swapped
Person jim(.name_="Jim", .age_=34, .city_="NYC"); // (ERR) not parenthesis
Godbolt Listing lst-0005-godb.cpp, https://godbolt.org/z/K8WePajvM:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/K8WePajvM
Person john1 {"John", 45, "Boston" }; // correct
Person john2 {"Boston", 45, "John" }; // oops, swapped, and no one notices
Person jack { .name_ = "Jack", .age_ = 23, .city_ = "Dallas" }; // okay
Person jimmi { .name_ = "Jimmi", .age_ = 48 }; // okay, not all specified
Person carl { "Carl", .age_ = 53 }; // (ERR) all designated or none
Person paul { .age = 34, .name_ = "Paul", .city = "Reno" }; // (ERR) swapped
Person jim(.name_="Jim", .age_=34, .city_="NYC"); // (ERR) not parenthesis
GodboltId:rrjPorT1o
Book listing lst-0006-book.cpp:
// https://godbolt.org/z/rrjPorT1o
// Snippet
Person create(string name, int age, string city) { // return type
Person result {name, age, city};
return result;
}
int main() {
Person john = create("John", 45, "Boston"); // store return value
print(john);
}
Godbolt Listing lst-0006-godb.cpp, https://godbolt.org/z/rrjPorT1o:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/rrjPorT1o
// Snippet
Person create(string name, int age, string city) { // return type
Person result {name, age, city};
return result;
}
int main() {
Person john = create("John", 45, "Boston"); // store return value
print(john);
}
GodboltId:ME76bfbWa
Book listing lst-0007-book.cpp:
// https://godbolt.org/z/ME76bfbWa
Person create(string name, int age, string city) {
return Person{name, age, city}; // returned directly
}
int main() {
print(create("John", 45, "Boston")); // return value used directly
}
Godbolt Listing lst-0007-godb.cpp, https://godbolt.org/z/ME76bfbWa:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/ME76bfbWa
Person create(string name, int age, string city) {
return Person{name, age, city}; // returned directly
}
int main() {
print(create("John", 45, "Boston")); // return value used directly
}
Listing 12.4: Here, specifying Person in the return is necessary.
Book listing lst-0010-book.cpp:
// https://godbolt.org/z/jer44aWs9
auto create(string name, int age, string city) {
return Person{name, age, city}; // auto requires constructor name
}
auto create2(string name, int age, string city) {
return {name, age, city}; // (ERR) auto with initializer_list does not work
}
Godbolt Listing lst-0010-godb.cpp, https://godbolt.org/z/jer44aWs9:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/jer44aWs9
auto create(string name, int age, string city) {
return Person{name, age, city}; // auto requires constructor name
}
auto create2(string name, int age, string city) {
return {name, age, city}; // (ERR) auto with initializer_list does not work
}
Listing 12.5: Methods bundle data and behavior together.
Book listing lst-0011-book.cpp:
// https://godbolt.org/z/5T1Er36P7
#include <string>
#include <iostream>
#include <format>
using std::string; using std::cout; using std::format;
struct Person {
string name_;
int age_;
string city_;
void print(); // function as a method of the type
};
void Person::print() { // method name is extended by Person::
cout << format("{} ({}) from {}\n",
name_, age_, city_); // in a method you can directly access fields
}
int main() {
Person john {"John", 45, "Boston" };
john.print(); // calling the method for a variable of the type
}
Godbolt Listing lst-0011-godb.cpp, https://godbolt.org/z/5T1Er36P7:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/5T1Er36P7
#include <string>
#include <iostream>
#include <format>
using std::string; using std::cout; using std::format;
struct Person {
string name_;
int age_;
string city_;
void print(); // function as a method of the type
};
void Person::print() { // method name is extended by Person::
cout << format("{} ({}) from {}\n",
name_, age_, city_); // in a method you can directly access fields
}
int main() {
Person john {"John", 45, "Boston" };
john.print(); // calling the method for a variable of the type
}
Listing 12.6: The “greeting()” method uses fields; via “this”, it always refers to the field belonging to the called object.
Book listing lst-0012-book.cpp:
struct Person {
//… rest as before …
string greeting();
};
string Person::greeting() {
return format("Hello {} from {}", this->name_, this->city_);
}
int main() {
Person anna { "Anna", 33, "Eek" };
Person nina { "Nina", 22, "Ojo" };
anna.greeting();
nina.greeting();
}
Listing 12.7: How to separate methods and data from each other.
Book listing lst-0014-book.cpp:
string greeting(Person * const p) { // implicit parameter made explicit
return format("Hello {} from {}", p->name_, p->city_);
}
GodboltId:hEvqWqqc6
Book listing lst-0015-book.cpp:
// https://godbolt.org/z/hEvqWqqc6
int value = 5; // global variable
struct Wrap {
int value = 3; // data field
void set(int value) { // parameter
this->value = value + ::value;
}
};
Godbolt Listing lst-0015-godb.cpp, https://godbolt.org/z/hEvqWqqc6:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/hEvqWqqc6
int value = 5; // global variable
struct Wrap {
int value = 3; // data field
void set(int value) { // parameter
this->value = value + ::value;
}
};
Listing 12.8: “print” takes a stream as an argument.
Book listing lst-0018-book.cpp:
// https://godbolt.org/z/bz6n3Pjrv
// Excerpt. Person as before
void Person::print(std::ostream& os) {
os << format("{} ({}) from {}", name_, age_, city_);
}
int main() {
Person carl {"Carl", 12, "Toledo"};
carl.print(cout); // on the screen
cout << "\n";
std::ofstream file {"persons.txt"};
carl.print(file); // to a file
// automatic test:
std::ostringstream oss{}; // writes to a string
carl.print(oss);
if(oss.str() == "Carl (12) from Toledo") {
cout << "ok\n";
} else {
cout << "Error in Person::print!\n";
return 1; // propagate error outward
}
}
Godbolt Listing lst-0018-godb.cpp, https://godbolt.org/z/bz6n3Pjrv:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/bz6n3Pjrv
// Excerpt. Person as before
void Person::print(std::ostream& os) {
os << format("{} ({}) from {}", name_, age_, city_);
}
int main() {
Person carl {"Carl", 12, "Toledo"};
carl.print(cout); // on the screen
cout << "\n";
std::ofstream file {"persons.txt"};
carl.print(file); // to a file
// automatic test:
std::ostringstream oss{}; // writes to a string
carl.print(oss);
if(oss.str() == "Carl (12) from Toledo") {
cout << "ok\n";
} else {
cout << "Error in Person::print!\n";
return 1; // propagate error outward
}
}
Listing 12.9: You can overload the standard operator for output.
Book listing lst-0019-book.cpp:
std::ostream& Person::print(std::ostream& os) {
return os << format("{} ({}) from {}", name_, age_, city_);
}
std::ostream& operator<<(std::ostream& os, Person p) {
return p.print(os);
}
Listing 12.10: The output with << is achieved by overloading a free function.
Book listing lst-0021-book.cpp:
// https://godbolt.org/z/a7q5xsWGj
// Excerpt …
std::ostream& print(std::ostream& os);
};
std::ostream& Person::print(std::ostream& os) {
return os << format("{} ({}) from {}", name_, age_, city_);
}
std::ostream& operator<<(std::ostream& os, Person p) {
return p.print(os);
}
int main() {
Person paul {"Paul", 23, "Irvine"};
cout << "You are " << paul << ", right?\n";
}
Godbolt Listing lst-0021-godb.cpp, https://godbolt.org/z/a7q5xsWGj:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/a7q5xsWGj
// Excerpt …
std::ostream& print(std::ostream& os);
};
std::ostream& Person::print(std::ostream& os) {
return os << format("{} ({}) from {}", name_, age_, city_);
}
std::ostream& operator<<(std::ostream& os, Person p) {
return p.print(os);
}
int main() {
Person paul {"Paul", 23, "Irvine"};
cout << "You are " << paul << ", right?\n";
}
Listing 12.11: Methods can also be defined inline.
Book listing lst-0022-book.cpp:
// https://godbolt.org/z/Y3eW1Kh8v
#include <string>
#include <iostream> // ostream
#include <format>
using std::string; using std::ostream; using std::format;
struct Person {
string name_;
int age_;
string city_;
ostream& print(ostream& os) { // method defined inline
return os << format("{} ({}) from {}", name_, age_, city_);
}
};
Godbolt Listing lst-0022-godb.cpp, https://godbolt.org/z/Y3eW1Kh8v:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Y3eW1Kh8v
#include <string>
#include <iostream> // ostream
#include <format>
using std::string; using std::ostream; using std::format;
struct Person {
string name_;
int age_;
string city_;
ostream& print(ostream& os) { // method defined inline
return os << format("{} ({}) from {}", name_, age_, city_);
}
};
Listing 12.12: How to define a constructor.
Book listing lst-0027-book.cpp:
struct Person {
string name_;
int age_;
string city_;
Person(); // declare constructor
};
Person::Person()
: name_{"no name"} // initialization value for name_
, age_{-1} // initialization value for age_
, city_{"no city"} // initialization value for city_
{ } // empty function body
Listing 12.13: Multiple constructors are also possible.
Book listing lst-0029-book.cpp:
// https://godbolt.org/z/zndbvh5nK
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(); // constructor without arguments
Person(sview n, int a, sview c); // constructor with three arguments
Person(sview n, int a); // constructor with two arguments
Person(sview n); // constructor with one argument
};
Person::Person()
: name_{"no name"}, age_{-1}, city_{"no city"} { }
Person::Person(sview n, int a, sview c)
: name_{n}, age_{a}, city_{c} { }
Person::Person(sview n, int a)
: name_{n}, age_{a}, city_{"no city"} { }
Person::Person(sview n)
: name_{n}, age_{-1}, city_{"no city"} { }
Godbolt Listing lst-0029-godb.cpp, https://godbolt.org/z/zndbvh5nK:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/zndbvh5nK
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(); // constructor without arguments
Person(sview n, int a, sview c); // constructor with three arguments
Person(sview n, int a); // constructor with two arguments
Person(sview n); // constructor with one argument
};
Person::Person()
: name_{"no name"}, age_{-1}, city_{"no city"} { }
Person::Person(sview n, int a, sview c)
: name_{n}, age_{a}, city_{c} { }
Person::Person(sview n, int a)
: name_{n}, age_{a}, city_{"no city"} { }
Person::Person(sview n)
: name_{n}, age_{-1}, city_{"no city"} { }
Listing 12.14: Member variables can be equipped with default values.
Book listing lst-0030-book.cpp:
// https://godbolt.org/z/Wo8zaq9WW
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_ = "no name";
int age_ = -1;
string city_ = "no city";
Person() {}
Person(sview n, int a, sview c)
: name_{n}, age_{a}, city_{c} { }
Person(sview n, int a)
: name_{n}, age_{a} { }
Person(sview n)
: name_{n} { }
};
Godbolt Listing lst-0030-godb.cpp, https://godbolt.org/z/Wo8zaq9WW:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Wo8zaq9WW
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_ = "no name";
int age_ = -1;
string city_ = "no city";
Person() {}
Person(sview n, int a, sview c)
: name_{n}, age_{a}, city_{c} { }
Person(sview n, int a)
: name_{n}, age_{a} { }
Person(sview n)
: name_{n} { }
};
Listing 12.15: A constructor can pass part of the initialization to another constructor.
Book listing lst-0031-book.cpp:
// https://godbolt.org/z/8h93f64Yj
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n, int a, sview c) // delegated constructor
: name_(n), age_(a), city_(c) { } // … implemented
Person() : Person{"no name",-1,"no city"} { } // delegating
Person(sview n, int a) : Person{n, a, "no city"} { } // delegating
Person(sview n) : Person{n, -1, "no city"} { } // delegating
};
Godbolt Listing lst-0031-godb.cpp, https://godbolt.org/z/8h93f64Yj:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/8h93f64Yj
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n, int a, sview c) // delegated constructor
: name_(n), age_(a), city_(c) { } // … implemented
Person() : Person{"no name",-1,"no city"} { } // delegating
Person(sview n, int a) : Person{n, a, "no city"} { } // delegating
Person(sview n) : Person{n, -1, "no city"} { } // delegating
};
Listing 12.16: A constructor can also be overloaded with default parameters.
Book listing lst-0032-book.cpp:
// https://godbolt.org/z/Gfrd5cceG
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n = "N.N.", int a = 18, sview c = "Berlin")
: name_(n), age_(a), city_(c) { }
};
Godbolt Listing lst-0032-godb.cpp, https://godbolt.org/z/Gfrd5cceG:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Gfrd5cceG
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n = "N.N.", int a = 18, sview c = "Berlin")
: name_(n), age_(a), city_(c) { }
};
Listing 12.17: A constructor with all preset arguments becomes the default constructor.
Book listing lst-0034-book.cpp:
// https://godbolt.org/z/1fE15Kf39
#include <vector>
#include <string>
#include <string_view>
#include <iostream>
using std::string; using std::cout; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
// acts as the default constructor:
Person(sview n = "N.N.", int a = 18, sview c = "Berlin")
: name_(n), age_(a), city_(c) { }
};
int main() {
std::vector<Person> people{}; // initially empty
people.resize(5); // expand to five 'empty' people
cout << people[3].city_ << "\n"; // Output: Berlin
}
Godbolt Listing lst-0034-godb.cpp, https://godbolt.org/z/1fE15Kf39:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/1fE15Kf39
#include <vector>
#include <string>
#include <string_view>
#include <iostream>
using std::string; using std::cout; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
// acts as the default constructor:
Person(sview n = "N.N.", int a = 18, sview c = "Berlin")
: name_(n), age_(a), city_(c) { }
};
int main() {
std::vector<Person> people{}; // initially empty
people.resize(5); // expand to five 'empty' people
cout << people[3].city_ << "\n"; // Output: Berlin
}
Listing 12.18: Do not call an initializing method in the constructor body.
Book listing lst-0035-book.cpp:
// https://godbolt.org/z/dzjaf6nbb
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n, int a, sview c)
{ // (ERR) Initialization list missing
init(n, a, c); // (ERR) questionable »initialization call«
}
void init(sview n, int a, sview c) {
name_ = n; age_ = a; city_ = c;
}
};
Godbolt Listing lst-0035-godb.cpp, https://godbolt.org/z/dzjaf6nbb:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/dzjaf6nbb
#include <string>
#include <string_view>
using std::string; using sview = std::string_view;
struct Person {
string name_;
int age_;
string city_;
Person(sview n, int a, sview c)
{ // (ERR) Initialization list missing
init(n, a, c); // (ERR) questionable »initialization call«
}
void init(sview n, int a, sview c) {
name_ = n; age_ = a; city_ = c;
}
};
Listing 12.19: External access to the data fields of a type.
Book listing lst-0037-book.cpp:
ostream& operator<<(ostream& os, Person p) {
return os << format("{} ({}) from {}", p.name_, p.age_, p.city_);
}
Listing 12.20: Divide a type into multiple sections with “public” and “private”.
Book listing lst-0038-book.cpp:
// https://godbolt.org/z/4MzxaevKa
#include <string>
#include <string_view>
using std::string; using std::string_view;
struct Person {
private: // everything from here cannot be used externally
string name_;
int age_;
string city_;
public: // everything from here can be used externally
Person(string_view n, int a, string_view c)
: name_{n}, age_{a}, city_{c} { }
void print();
};
Godbolt Listing lst-0038-godb.cpp, https://godbolt.org/z/4MzxaevKa:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/4MzxaevKa
#include <string>
#include <string_view>
using std::string; using std::string_view;
struct Person {
private: // everything from here cannot be used externally
string name_;
int age_;
string city_;
public: // everything from here can be used externally
Person(string_view n, int a, string_view c)
: name_{n}, age_{a}, city_{c} { }
void print();
};
Listing 12.21: A “class” starts with private visibility.
Book listing lst-0039-book.cpp:
// https://godbolt.org/z/MYzsqfqdo
#include <string>
#include <string_view>
using std::string; using std::string_view;
class Person { // a class starts with private visibility
string name_;
int age_;
string city_;
public: // everything from here can be used externally
Person(string_view n, int a, string_view c)
: name_{n}, age_{a}, city_{c} { }
void print();
};
Godbolt Listing lst-0039-godb.cpp, https://godbolt.org/z/MYzsqfqdo:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/MYzsqfqdo
#include <string>
#include <string_view>
using std::string; using std::string_view;
class Person { // a class starts with private visibility
string name_;
int age_;
string city_;
public: // everything from here can be used externally
Person(string_view n, int a, string_view c)
: name_{n}, age_{a}, city_{c} { }
void print();
};
Listing 12.22: Parts of the data are private.
Book listing lst-0040-book.cpp:
// https://godbolt.org/z/Pr76Y55P4
class Rect {
int area_; // private data
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
};
Godbolt Listing lst-0040-godb.cpp, https://godbolt.org/z/Pr76Y55P4:
//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Pr76Y55P4
class Rect {
int area_; // private data
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
};
Listing 12.23: With “= default”, you let the compiler generate code.
Book listing lst-0041-book.cpp:
// https://godbolt.org/z/PaG9nTs7s
class Rect {
int area_; // private data
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
Rect() = default; // let the compiler generate a constructor
};
class Pow {
int result_; // private data; holds 'base' raised to 'exp'
public:
int base_, exp_;
void set(int b, int e) { /* ... */ }
int calc() { return result_; }
Pow() : result_{1},base_{1},exp_{1} {} // initialize sensibly
};
Godbolt Listing lst-0041-godb.cpp, https://godbolt.org/z/PaG9nTs7s:
//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/PaG9nTs7s
class Rect {
int area_; // private data
public:
int x_, y_;
void set(int x, int y) { x_=x; y_=y; area_=x_*y_; }
int calc() { return area_; }
Rect() = default; // let the compiler generate a constructor
};
class Pow {
int result_; // private data; holds 'base' raised to 'exp'
public:
int base_, exp_;
void set(int b, int e) { /* ... */ }
int calc() { return result_; }
Pow() : result_{1},base_{1},exp_{1} {} // initialize sensibly
};
Listing 12.24: Custom data types can protect against errors. This is the first step toward that.
Book listing lst-0042-book.cpp:
// https://godbolt.org/z/aGd56xzsT
#include <string> // string, stoi
#include <iostream> // cin, cout, ostream
#include <format>
using std::ostream; using std::format;
class Year { /* Helper types for safe date */
int value_; // e.g., 2024
public:
Year(int v) : value_{v} {}
int value() { return value_; }
};
class Month {
int value_; // 1..12
public:
Month(int v) : value_{v} {}
int value() { return value_; }
};
class Day {
int value_; // 1..31
public:
Day(int v) : value_{v} {}
int value() { return value_; }
};
/* type-safe constructing date */
class Date {
Year year_;
Month month_ = 1;
Day day_ = 1;
public:
Date(int y) : year_{y} // 1-argument constructor
{} // sets 1st January of the specified year
Date(Year y, Month m, Day d) // 3-argument constructor
: year_{y}, month_{m}, day_{d}
{}
ostream& print(ostream& os); // e.g., 2024-04-20
};
ostream& Date::print(ostream& os) { // e.g., 2024-04-20
return os << format("{}-{:02}-{:02}",
year_.value(), month_.value(), day_.value());
}
ostream& operator<<(ostream& os, Date d) {
return d.print(os);
}
// http://codegolf.stackexchange.com/a/11146/1405, user Fors, 2014-02-25
Date easter(Year year) {
const int y = year.value();
int a = y/100*1483 - y/400*2225 + 2613;
int b = (y%19*3510 + a/25*319)/330%29;
b = 148 - b - (y*5/4 + a - b)%7;
return Date{Year{y}, Month{b/31}, Day{b%31 + 1}}; // Create date
}
int main(int argc, const char *argv[] ) {
/* Input */
int number {};
if(argc > 1) {
number = std::stoi(argv[1]);
} else {
std::cout << "Year? "; std::cin >> number;
}
/* Calculation */
Date date = easter(number); // implicit conversion to Year
/* Output */
std::cout << "Easter: " << date << "\n";
}
Godbolt Listing lst-0042-godb.cpp, https://godbolt.org/z/aGd56xzsT:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/aGd56xzsT
#include <string> // string, stoi
#include <iostream> // cin, cout, ostream
#include <format>
using std::ostream; using std::format;
class Year { /* Helper types for safe date */
int value_; // e.g., 2024
public:
Year(int v) : value_{v} {}
int value() { return value_; }
};
class Month {
int value_; // 1..12
public:
Month(int v) : value_{v} {}
int value() { return value_; }
};
class Day {
int value_; // 1..31
public:
Day(int v) : value_{v} {}
int value() { return value_; }
};
/* type-safe constructing date */
class Date {
Year year_;
Month month_ = 1;
Day day_ = 1;
public:
Date(int y) : year_{y} // 1-argument constructor
{} // sets 1st January of the specified year
Date(Year y, Month m, Day d) // 3-argument constructor
: year_{y}, month_{m}, day_{d}
{}
ostream& print(ostream& os); // e.g., 2024-04-20
};
ostream& Date::print(ostream& os) { // e.g., 2024-04-20
return os << format("{}-{:02}-{:02}",
year_.value(), month_.value(), day_.value());
}
ostream& operator<<(ostream& os, Date d) {
return d.print(os);
}
// http://codegolf.stackexchange.com/a/11146/1405, user Fors, 2014-02-25
Date easter(Year year) {
const int y = year.value();
int a = y/100*1483 - y/400*2225 + 2613;
int b = (y%19*3510 + a/25*319)/330%29;
b = 148 - b - (y*5/4 + a - b)%7;
return Date{Year{y}, Month{b/31}, Day{b%31 + 1}}; // Create date
}
int main(int argc, const char *argv[] ) {
/* Input */
int number {};
if(argc > 1) {
number = std::stoi(argv[1]);
} else {
std::cout << "Year? "; std::cin >> number;
}
/* Calculation */
Date date = easter(number); // implicit conversion to Year
/* Output */
std::cout << "Easter: " << date << "\n";
}
Listing 12.25: If all “return” statements return the same variable, the compiler can always avoid a copy.
Book listing lst-0051-book.cpp:
// https://godbolt.org/z/T7afh4vxz
#include <vector>
std::vector<int> createData(unsigned size) {
std::vector<int> result{};
for(int idx=0; idx<size; ++idx) {
result.push_back(idx);
}
return result;
}
Godbolt Listing lst-0051-godb.cpp, https://godbolt.org/z/T7afh4vxz:
//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/T7afh4vxz
#include <vector>
std::vector<int> createData(unsigned size) {
std::vector<int> result{};
for(int idx=0; idx<size; ++idx) {
result.push_back(idx);
}
return result;
}
Listing 12.26: With “explicit”, you prevent automatic type conversion.
Book listing lst-0060-book.cpp:
// only excerpts
class Year {
explicit Year(int v) : value_{v} {}
};
class Month {
explicit Month(int v) : value_{v} {}
};
class Day {
explicit Day(int v) : value_{v} {}
};
class Date {
explicit Date(int y) : year_{y} {}
};
Listing 12.27: “Year” no longer has “value()” and requires other methods.
Book listing lst-0070-book.cpp:
// https://godbolt.org/z/EPz4Kd6jP
class Year {
int value_;
public:
explicit Year(int v) : value_{v} {}
std::ostream& print(std::ostream& os) const;
Year& advance(const Year& other);
bool equals(const Year& other) const;
bool less_than(const Year& other) const;
};
std::ostream& Year::print(std::ostream& os) const {
return os << value_;
}
std::ostream& operator<<(std::ostream& os, const Year& year) {
return year.print(os);
}
Year& Year::advance(const Year& other) {
value_ += other.value_;
return *this;
}
bool Year::equals(const Year& other) const {
return value_ == other.value_;
}
bool Year::less_than(const Year& other) const {
return value_ < other.value_;
}
Godbolt Listing lst-0070-godb.cpp, https://godbolt.org/z/EPz4Kd6jP:
//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/EPz4Kd6jP
class Year {
int value_;
public:
explicit Year(int v) : value_{v} {}
std::ostream& print(std::ostream& os) const;
Year& advance(const Year& other);
bool equals(const Year& other) const;
bool less_than(const Year& other) const;
};
std::ostream& Year::print(std::ostream& os) const {
return os << value_;
}
std::ostream& operator<<(std::ostream& os, const Year& year) {
return year.print(os);
}
Year& Year::advance(const Year& other) {
value_ += other.value_;
return *this;
}
bool Year::equals(const Year& other) const {
return value_ == other.value_;
}
bool Year::less_than(const Year& other) const {
return value_ < other.value_;
}
Listing 12.28: If “advance” returns the object itself, then you can call another method afterward.
Book listing lst-0071-book.cpp:
Year year{2024};
year.advance(Year{1}).advance(Year{3});
cout << year; // Output: 2028
Listing 12.29: A fluent programming interface sometimes allows for clear code.
Book listing lst-0072-book.cpp:
Page page = Html().body()
.h1("Heading")
.table().border(0)
.tr()
.td().css("head").text("Dog Breed").end()
.td().text("Poodle").end()
.end()
.end()
.toPage();
Listing 12.30: The internal type “int” has become part of the class interface.
Book listing lst-0073-book.cpp:
// https://godbolt.org/z/hbsbMsGr1
class Year {
int value_; // actually internally used type
public:
explicit Year(int v) : value_{v} {} // type becomes part of the interface
int value() { return value_; } // also in the return
};
int main() {
Year year{ 2024 }; // type int
int val = year.value(); // matching type
}
Godbolt Listing lst-0073-godb.cpp, https://godbolt.org/z/hbsbMsGr1:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/hbsbMsGr1
class Year {
int value_; // actually internally used type
public:
explicit Year(int v) : value_{v} {} // type becomes part of the interface
int value() { return value_; } // also in the return
};
int main() {
Year year{ 2024 }; // type int
int val = year.value(); // matching type
}
Listing 12.31: With “using”, you can introduce type aliases that make it easier to maintain interfaces than with the types themselves.
Book listing lst-0074-book.cpp:
// https://godbolt.org/z/EYjo8E8Gn
class Year {
public:
using value_type = int; // introduce type alias
value_type value_; // actually internally used type
public:
explicit Year(value_type v) : value_{v} {}
value_type value() { return value_; }
};
int main() {
Year year{ 2024 }; // rely on compiler conversion here
Year::value_type val = year.value(); // use ::
}
Godbolt Listing lst-0074-godb.cpp, https://godbolt.org/z/EYjo8E8Gn:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/EYjo8E8Gn
class Year {
public:
using value_type = int; // introduce type alias
value_type value_; // actually internally used type
public:
explicit Year(value_type v) : value_{v} {}
value_type value() { return value_; }
};
int main() {
Year year{ 2024 }; // rely on compiler conversion here
Year::value_type val = year.value(); // use ::
}
Listing 12.32: The standard library also contains many useful type aliases.
Book listing lst-0075-book.cpp:
// https://godbolt.org/z/zdMfoa7Yj
#include <vector>
#include <set>
#include <iostream>
using std::vector; using std::set; using std::cout;
using vector_t = vector<unsigned long long>; // Your own type alias
int main() {
vector_t huge{ 12ULL, 10'000'000'000ULL, 9ULL, 0ULL, };
vector_t::size_type sz = huge.size();
vector_t::value_type uiuiui = huge[1];
for(vector_t::iterator it = huge.begin(); it != huge.end(); ++it)
*it *= 2; // double
/* sort via set */
set<vector_t::value_type> sorted{huge.begin(), huge.end()};
for(vector_t::value_type val : sorted)
cout << val << " ";
cout << "\n";
}
Godbolt Listing lst-0075-godb.cpp, https://godbolt.org/z/zdMfoa7Yj:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/zdMfoa7Yj
#include <vector>
#include <set>
#include <iostream>
using std::vector; using std::set; using std::cout;
using vector_t = vector<unsigned long long>; // Your own type alias
int main() {
vector_t huge{ 12ULL, 10'000'000'000ULL, 9ULL, 0ULL, };
vector_t::size_type sz = huge.size();
vector_t::value_type uiuiui = huge[1];
for(vector_t::iterator it = huge.begin(); it != huge.end(); ++it)
*it *= 2; // double
/* sort via set */
set<vector_t::value_type> sorted{huge.begin(), huge.end()};
for(vector_t::value_type val : sorted)
cout << val << " ";
cout << "\n";
}
Listing 12.33: When initializing a variable, the compiler can determine the type.
Book listing lst-0079-book.cpp:
// https://godbolt.org/z/Wh71cf7j1
#include <vector>
#include <set>
#include <iostream> // cout
using std::vector; using std::set; using std::cout;
using vector_t = vector<unsigned long long>; // Your own type alias
int main() {
vector_t huge{ 12ULL, 10000000000ULL, 9ULL, 0ULL, };
auto sz = huge.size();
auto uiuiui = huge[1];
for(auto it = huge.begin(); it != huge.end(); ++it)
*it *= 2; // double
/* sort via set */
set sorted(huge.begin(), huge.end()); // set<vector_t::value_type>
for(auto val : sorted)
cout << val << " ";
cout << "\n";
}
Godbolt Listing lst-0079-godb.cpp, https://godbolt.org/z/Wh71cf7j1:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Wh71cf7j1
#include <vector>
#include <set>
#include <iostream> // cout
using std::vector; using std::set; using std::cout;
using vector_t = vector<unsigned long long>; // Your own type alias
int main() {
vector_t huge{ 12ULL, 10000000000ULL, 9ULL, 0ULL, };
auto sz = huge.size();
auto uiuiui = huge[1];
for(auto it = huge.begin(); it != huge.end(); ++it)
*it *= 2; // double
/* sort via set */
set sorted(huge.begin(), huge.end()); // set<vector_t::value_type>
for(auto val : sorted)
cout << val << " ";
cout << "\n";
}
Listing 12.34: Type deduction with “auto” can be further restricted using a concept.
Book listing lst-0080-book.cpp:
// https://godbolt.org/z/qzWchWoeK
#include <vector>
#include <set>
#include <iostream> // cout
#include <concepts> // integral
#include <iterator> // output_iterator, input_iterator
using namespace std;
using vector_t = vector<unsigned long long>; // your own type alias
int main() {
vector_t huge{ 12ULL, 10000000000ULL, 9ULL, 0ULL, };
unsigned_integral auto sz = huge.size();
unsigned_integral auto uiuiui = huge[1];
signed_integral auto meh = huge[1]; // (ERR) concept not fulfilled
input_or_output_iterator auto itx = huge.begin(); // concept without parameter
for(output_iterator<unsigned long long> auto it=huge.begin();
it!=huge.end(); ++it)
*it *= 2; // double
/* sort using set */
set sorted(huge.begin(), huge.end()); // set<vector_t::value_type>
for(const unsigned_integral auto& val : sorted)
cout << val << " ";
cout << "\n";
}
Godbolt Listing lst-0080-godb.cpp, https://godbolt.org/z/qzWchWoeK:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/qzWchWoeK
#include <vector>
#include <set>
#include <iostream> // cout
#include <concepts> // integral
#include <iterator> // output_iterator, input_iterator
using namespace std;
using vector_t = vector<unsigned long long>; // your own type alias
int main() {
vector_t huge{ 12ULL, 10000000000ULL, 9ULL, 0ULL, };
unsigned_integral auto sz = huge.size();
unsigned_integral auto uiuiui = huge[1];
signed_integral auto meh = huge[1]; // (ERR) concept not fulfilled
input_or_output_iterator auto itx = huge.begin(); // concept without parameter
for(output_iterator<unsigned long long> auto it=huge.begin();
it!=huge.end(); ++it)
*it *= 2; // double
/* sort using set */
set sorted(huge.begin(), huge.end()); // set<vector_t::value_type>
for(const unsigned_integral auto& val : sorted)
cout << val << " ";
cout << "\n";
}
Listing 12.35: When you enrich “auto” with &, you get a modifiable reference.
Book listing lst-0081-book.cpp:
// https://godbolt.org/z/ro6fdsvT9
#include <vector>
#include <iostream> // cout
using std::vector; using std::cout;
int main() {
vector data{ 12, 100, -1, 0, }; // vector<int>
for(auto& val : data)
val *= 2; // double it
for(const auto val : data)
cout << val << " ";
cout << "\n";
}
Godbolt Listing lst-0081-godb.cpp, https://godbolt.org/z/ro6fdsvT9:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/ro6fdsvT9
#include <vector>
#include <iostream> // cout
using std::vector; using std::cout;
int main() {
vector data{ 12, 100, -1, 0, }; // vector<int>
for(auto& val : data)
val *= 2; // double it
for(const auto val : data)
cout << val << " ";
cout << "\n";
}
Listing 12.36: To pack a custom data type into a “vector”, it does not need to meet many requirements.
Book listing lst-0085-book.cpp:
// https://godbolt.org/z/8ejPs6zr6
#include <vector>
struct Number {
int value_ = 0;
Number() {} // Default constructor
explicit Number(int v) : value_{v} {}
};
int main() {
std::vector<Number> numbers{}; // okay: Number meets the requirements
numbers.push_back( Number{2} );
}
Godbolt Listing lst-0085-godb.cpp, https://godbolt.org/z/8ejPs6zr6:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/8ejPs6zr6
#include <vector>
struct Number {
int value_ = 0;
Number() {} // Default constructor
explicit Number(int v) : value_{v} {}
};
int main() {
std::vector<Number> numbers{}; // okay: Number meets the requirements
numbers.push_back( Number{2} );
}
Listing 12.37: For a “set” of a custom data type, you need to override “operator<”.
Book listing lst-0086-book.cpp:
// https://godbolt.org/z/Yhzqo6dxT
#include <set>
struct Number {
int value_ = 0;
explicit Number(int v) : value_{v} {}
};
bool operator<(const Number& left, const Number& right) {
return left.value_ < right.value_;
}
int main() {
std::set<Number> numbers{}; // okay
numbers.insert( Number{3} ); // operator< is needed here
}
Godbolt Listing lst-0086-godb.cpp, https://godbolt.org/z/Yhzqo6dxT:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Yhzqo6dxT
#include <set>
struct Number {
int value_ = 0;
explicit Number(int v) : value_{v} {}
};
bool operator<(const Number& left, const Number& right) {
return left.value_ < right.value_;
}
int main() {
std::set<Number> numbers{}; // okay
numbers.insert( Number{3} ); // operator< is needed here
}
GodboltId:TdEnhKxGK
Book listing lst-0087-book.cpp:
// https://godbolt.org/z/TdEnhKxGK
#include <map>
struct Number {
int value_ = 0;
explicit Number(int v) : value_{v} {}
};
bool operator<(const Number& left, const Number& right) {
return left.value_ < right.value_;
}
int main() {
std::map<Number,int> numbers{}; // okay
numbers.insert( std::make_pair(Number{4},100) ); // needs operator<
numbers[Number{5}] = 200; // here as well
}
Godbolt Listing lst-0087-godb.cpp, https://godbolt.org/z/TdEnhKxGK:
//#(compile) c++; compiler:g141; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/TdEnhKxGK
#include <map>
struct Number {
int value_ = 0;
explicit Number(int v) : value_{v} {}
};
bool operator<(const Number& left, const Number& right) {
return left.value_ < right.value_;
}
int main() {
std::map<Number,int> numbers{}; // okay
numbers.insert( std::make_pair(Number{4},100) ); // needs operator<
numbers[Number{5}] = 200; // here as well
}