listings-Chap16-README-onepage.md
Listings of Chap16.docx
This is the list of listings on one page. You can also view a linked summary.
Listing 16.1: Which variables can you use for “x1” to “x5”?
Book listing lst-0001-book.cpp:
// https://godbolt.org/z/9jGKPbzqo
struct MyValue { /* something */ };
MyValue globalValue{}; // global class instance
void function(const MyValue ¶mRef) {
if( /*...*/ ) function( /*x1?*/ ); // call some function
MyValue localValue{}; // local class instance
} // end of function
int main() {
MyValue mvalue1{};
function( /*x2?*/ );
function( MyValue{} ); // temporary value
{
MyValue mvalue2{};
function( /*x3?*/ );
MyValue mvalue3{};
} // end of inner block
function( /*x4?*/ );
MyValue mvalue4{};
function( /*x5?*/ );
} // end of main function
Godbolt Listing lst-0001-godb.cpp, https://godbolt.org/z/9jGKPbzqo:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/9jGKPbzqo
struct MyValue { /* something */ };
MyValue globalValue{}; // global class instance
void function(const MyValue ¶mRef) {
if( /*...*/ ) function( /*x1?*/ ); // call some function
MyValue localValue{}; // local class instance
} // end of function
int main() {
MyValue mvalue1{};
function( /*x2?*/ );
function( MyValue{} ); // temporary value
{
MyValue mvalue2{};
function( /*x3?*/ );
MyValue mvalue3{};
} // end of inner block
function( /*x4?*/ );
MyValue mvalue4{};
function( /*x5?*/ );
} // end of main function
Listing 16.2: When you call a function with a parameter for which the compiler invokes a constructor to perform a conversion, it creates a temp-value.
Book listing lst-0002-book.cpp:
// https://godbolt.org/z/eK1Ervjs5
#include <string>
#include <iostream> // cout
using std::string; using std::cout;
struct Value {
int value_;
Value(int value) // 1-arg constructor = type conversion
: value_{value} {}
};
size_t length(string arg) {
return arg.size();
}
Value twice(Value v) {
return Value{ v.value_*2 };
}
int main() {
cout << length("Hipphopp") << "\n"; // const char* to string
cout << twice(10).value_ << "\n"; // int to Value
string name {"Gandalf"};
cout << ( name + " the Grey" ) << "\n"; // string + const char*
}
Godbolt Listing lst-0002-godb.cpp, https://godbolt.org/z/eK1Ervjs5:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/eK1Ervjs5
#include <string>
#include <iostream> // cout
using std::string; using std::cout;
struct Value {
int value_;
Value(int value) // 1-arg constructor = type conversion
: value_{value} {}
};
size_t length(string arg) {
return arg.size();
}
Value twice(Value v) {
return Value{ v.value_*2 };
}
int main() {
cout << length("Hipphopp") << "\n"; // const char* to string
cout << twice(10).value_ << "\n"; // int to Value
string name {"Gandalf"};
cout << ( name + " the Grey" ) << "\n"; // string + const char*
}
Listing 16.3: A destructor is executed when an object is removed.
Book listing lst-0003-book.cpp:
// https://godbolt.org/z/WPcTv3zKM
#include <string>
#include <iostream>
#include <iomanip> // setw
using std::cout; using std::setw; using std::string;
struct MyValue {
static int counter; // static: exists only once for all instances
int number_; // indentation level of this instance for output
string name_; // name of this instance for output
explicit MyValue(string name)
: number_{++counter} // increment indentation level per instance
, name_{name} // remember the object's name for output
{
cout << setw(number_) << " " // use number_ for indentation
<< "Constructor " << name_ << "\n"; // output instance name
}
~MyValue() { // destructor
cout << setw(number_) << " " << "Destructor " << name_ << "\n";
}
};
int MyValue::counter = 0; // initialization of the static class variable
Godbolt Listing lst-0003-godb.cpp, https://godbolt.org/z/WPcTv3zKM:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/WPcTv3zKM
#include <string>
#include <iostream>
#include <iomanip> // setw
using std::cout; using std::setw; using std::string;
struct MyValue {
static int counter; // static: exists only once for all instances
int number_; // indentation level of this instance for output
string name_; // name of this instance for output
explicit MyValue(string name)
: number_{++counter} // increment indentation level per instance
, name_{name} // remember the object's name for output
{
cout << setw(number_) << " " // use number_ for indentation
<< "Constructor " << name_ << "\n"; // output instance name
}
~MyValue() { // destructor
cout << setw(number_) << " " << "Destructor " << name_ << "\n";
}
};
int MyValue::counter = 0; // initialization of the static class variable
Listing 16.4: Here, many objects are created and destroyed.
Book listing lst-0004-book.cpp:
// https://godbolt.org/z/ennEoWPzb
void function(const MyValue ¶mRef) {
MyValue localValue{"local"};
}
int main() {
MyValue mvalue1{"mvalue1"};
function( MyValue{"temp"} );
function( mvalue1 );
{
MyValue mvalue2{"mvalue2"};
}
}
Godbolt Listing lst-0004-godb.cpp, https://godbolt.org/z/ennEoWPzb:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/ennEoWPzb
void function(const MyValue ¶mRef) {
MyValue localValue{"local"};
}
int main() {
MyValue mvalue1{"mvalue1"};
function( MyValue{"temp"} );
function( mvalue1 );
{
MyValue mvalue2{"mvalue2"};
}
}
Listing 16.5: This output shows when objects are created and destroyed.
Book listing lst-0005-book.cpp:
Constructor mvalue1
Constructor temp
Constructor local
Destructor local
Destructor temp
Constructor local
Destructor local
Constructor mvalue2
Destructor mvalue2
Destructor mvalue1
Listing 16.6: A simple example of a C interface to a resource.
Book listing lst-0006-book.cpp:
// https://godbolt.org/z/54dzsb31z
#ifndef DATABASE_HPP
#define DATABASE_HPP
typedef void* db_handle_t;
db_handle_t db_open(const char* filename);
void db_close(db_handle_t db);
int db_execute(db_handle_t db, const char* query);
#endif
Godbolt Listing lst-0006-godb.cpp, https://godbolt.org/z/54dzsb31z:
//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/54dzsb31z
#ifndef DATABASE_HPP
#define DATABASE_HPP
typedef void* db_handle_t;
db_handle_t db_open(const char* filename);
void db_close(db_handle_t db);
int db_execute(db_handle_t db, const char* query);
#endif
Listing 16.7: If you need to close a resource, the destructor is suitable for this.
Book listing lst-0007-book.cpp:
// https://godbolt.org/z/6399qK9Ts
#include <iostream> // cout
#include "database.hpp" // include the foreign API
class Database {
db_handle_t db_; // wrapped resource
public:
Database(const char* filename);
~Database();
int execute(const char* query);
};
Database::Database(const char* filename)
: db_{db_open(filename)} // requesting the resource
{ }
Database::~Database() {
db_close(db_); // releasing the resource
}
int Database::execute(const char* query) {
return db_execute(db_, query); // using the resource
}
int main() {
Database db{"customers.dat"}; // creating the wrapper
std::cout << "Count: "<< db.execute("select * from cust") << "\n";
} // automatically removing the wrapper
Godbolt Listing lst-0007-godb.cpp, https://godbolt.org/z/6399qK9Ts:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/6399qK9Ts
#include <iostream> // cout
#include "database.hpp" // include the foreign API
class Database {
db_handle_t db_; // wrapped resource
public:
Database(const char* filename);
~Database();
int execute(const char* query);
};
Database::Database(const char* filename)
: db_{db_open(filename)} // requesting the resource
{ }
Database::~Database() {
db_close(db_); // releasing the resource
}
int Database::execute(const char* query) {
return db_execute(db_, query); // using the resource
}
int main() {
Database db{"customers.dat"}; // creating the wrapper
std::cout << "Count: "<< db.execute("select * from cust") << "\n";
} // automatically removing the wrapper
Listing 16.8: The constructor initializes or throws an exception.
Book listing lst-0008-book.cpp:
Database::Database(const char* filename)
: db_{ db_open(filename) }
{
if(nullptr == db_) { // Error opening
throw IllegalArgumentException("Error opening the DB");
}
}
Listing 16.9: Examples of Yoda conditions with “==”.
Book listing lst-0010-book.cpp:
if("Yoda" == character) …
if(42 == answer) …
Listing 16.10: A Yoda condition with a method call.
Book listing lst-0011-book.cpp:
#include "my_string.hpp"
static const my_string ZEBRA { "zebra" };
int main() {
my_string animal{ "horse" };
if(ZEBRA.equals(animal)) return 0;
else return 1;
}
Listing 16.11: The constructor of “CanThrow” can terminate with an exception.
Book listing lst-0012-book.cpp:
// https://godbolt.org/z/99PPn595E
#include <iostream> // cout
#include <stdexcept> // runtime_error
struct CanThrow {
CanThrow(int whatShouldHappen) {
std::cout << "Constructor " << whatShouldHappen << "...\n";
if(whatShouldHappen == 666)
throw std::runtime_error("Test error");
std::cout << "...Constructor finished\n";
}
~CanThrow() {
std::cout << "Destructor.\n";
}
};
int main() {
try {
CanThrow ct1{0}; // okay, does not throw an exception
} catch(std::runtime_error &exc) {
std::cout << "Caught-1: " << exc.what() << "\n";
}
try {
CanThrow ct2{666}; // throws, ct2 is not created
} catch(std::runtime_error &exc) {
std::cout << "Caught-2: " << exc.what() << "\n";
}
}
Godbolt Listing lst-0012-godb.cpp, https://godbolt.org/z/99PPn595E:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/99PPn595E
#include <iostream> // cout
#include <stdexcept> // runtime_error
struct CanThrow {
CanThrow(int whatShouldHappen) {
std::cout << "Constructor " << whatShouldHappen << "...\n";
if(whatShouldHappen == 666)
throw std::runtime_error("Test error");
std::cout << "...Constructor finished\n";
}
~CanThrow() {
std::cout << "Destructor.\n";
}
};
int main() {
try {
CanThrow ct1{0}; // okay, does not throw an exception
} catch(std::runtime_error &exc) {
std::cout << "Caught-1: " << exc.what() << "\n";
}
try {
CanThrow ct2{666}; // throws, ct2 is not created
} catch(std::runtime_error &exc) {
std::cout << "Caught-2: " << exc.what() << "\n";
}
}
Listing 16.12: Partially initialized data fields are also cleaned up in the case of an exception.
Book listing lst-0013-book.cpp:
class Mega {
std::vector<int> data_;
CanThrow canThrow_;
std::map<string,int> more_;
public:
Mega()
: data_{}
, canThrow_{666} // triggers an exception
, more_{}
{ }
};
Listing 16.13: Passing by value creates objects with the compiler-generated copy constructor.
Book listing lst-0014-book.cpp:
// https://godbolt.org/z/3T869arc8
void function(MyValue paramValue) {
std::cout << "(function)\n";
MyValue localValue{"local"};
}
int main() {
MyValue mvalue1{"mvalue1"};
function( MyValue{"temp"} );
function( mvalue1 );
{
MyValue mvalue2{"mvalue2"};
}
}
Godbolt Listing lst-0014-godb.cpp, https://godbolt.org/z/3T869arc8:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/3T869arc8
void function(MyValue paramValue) {
std::cout << "(function)\n";
MyValue localValue{"local"};
}
int main() {
MyValue mvalue1{"mvalue1"};
function( MyValue{"temp"} );
function( mvalue1 );
{
MyValue mvalue2{"mvalue2"};
}
}
Listing 16.14: The copy constructor takes a constant reference of the class as an argument.
Book listing lst-0015-book.cpp:
// https://godbolt.org/z/sGan39a8z
struct MyValue {
static int counter;
int number_;
string name_;
explicit MyValue(string name) // as before
: number_{++counter} , name_{name}
{ cout << setw(number_) << '_' << "Constructor " << name_ << "\n"; }
MyValue(const MyValue &orig) // new copy constructor
: number_{++counter} , name_{orig.name_ + "-Copy"}
{ cout << setw(number_)<<" " << "Copy Constructor " << name_ << "\n"; }
~MyValue() { // as before
cout << setw(number_)<<" " << "Destructor " << name_ << "\n";
}
};
int MyValue::counter = 0;
Godbolt Listing lst-0015-godb.cpp, https://godbolt.org/z/sGan39a8z:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/sGan39a8z
struct MyValue {
static int counter;
int number_;
string name_;
explicit MyValue(string name) // as before
: number_{++counter} , name_{name}
{ cout << setw(number_) << '_' << "Constructor " << name_ << "\n"; }
MyValue(const MyValue &orig) // new copy constructor
: number_{++counter} , name_{orig.name_ + "-Copy"}
{ cout << setw(number_)<<" " << "Copy Constructor " << name_ << "\n"; }
~MyValue() { // as before
cout << setw(number_)<<" " << "Destructor " << name_ << "\n";
}
};
int MyValue::counter = 0;
Listing 16.15: Copy and assignment.
Book listing lst-0016-book.cpp:
void byVal(MyValue arg) { }
int main() {
MyValue value1{"ABCD"}; // new instance, constructed via string
MyValue value2{value1}; // new instance, constructed via copy
MyValue value3 = value1; // new instance, also via copy, despite =
byVal(value1); // a new instance via copy
value1 = value2; // not a new instance, but an assignment
}
GodboltId:5M8xKabjK
Book listing lst-0017-book.cpp:
// https://godbolt.org/z/5M8xKabjK
int main() {
MyValue value1{"ABCD"};
MyValue value2{"WXYZ"};
value1 = value2; // assignment
}
Godbolt Listing lst-0017-godb.cpp, https://godbolt.org/z/5M8xKabjK:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/5M8xKabjK
int main() {
MyValue value1{"ABCD"};
MyValue value2{"WXYZ"};
value1 = value2; // assignment
}
Listing 16.16: The schema for implementing a custom assignment operator.
Book listing lst-0019-book.cpp:
// https://godbolt.org/z/s65GvY9xj
struct MyValue {
// … everything else as before
MyValue& operator=(const MyValue& right) {
if(this != &right) { // 1. check for self-assignment
// 2. Release previous resources; none here
// 3. element-wise transfer by assignment or similar
name_ = right.name_ + "-Assignment (previously " + name_ + ")";
/* number_ remains, and thus the original indentation */
}
return *this; // 4. return self
}
};
Godbolt Listing lst-0019-godb.cpp, https://godbolt.org/z/s65GvY9xj:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/s65GvY9xj
struct MyValue {
// … everything else as before
MyValue& operator=(const MyValue& right) {
if(this != &right) { // 1. check for self-assignment
// 2. Release previous resources; none here
// 3. element-wise transfer by assignment or similar
name_ = right.name_ + "-Assignment (previously " + name_ + ")";
/* number_ remains, and thus the original indentation */
}
return *this; // 4. return self
}
};
Listing 16.17: The “const” data element is initially without an assignment operator.
Book listing lst-0020-book.cpp:
struct MyNumber {
const int number_; // constant data field
explicit MyNumber(int v)
: number_{v} // initialization of the constant data field
{}
};
int main() {
MyNumber c1{4};
MyNumber c2{7};
c1 = c2; // (ERR) Error: Assignment removed by compiler
}
Listing 16.18: Use “= delete” to manually remove operations.
Book listing lst-0021-book.cpp:
// https://godbolt.org/z/398GhcbaE
struct MyNumber {
int number_; // variable data field
explicit MyNumber(int v)
: number_{v} {}
MyNumber& operator=(const MyNumber&) = delete; // remove assignment
MyNumber(const MyNumber&) = delete; // remove copy
};
int main() {
MyNumber c1{4};
MyNumber c2{7};
c1 = c2; // (ERR) Error – assignment removed by programmer
MyNumber c3{c1}; // (ERR) Error – copy removed by programmer
}
Godbolt Listing lst-0021-godb.cpp, https://godbolt.org/z/398GhcbaE:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/398GhcbaE
struct MyNumber {
int number_; // variable data field
explicit MyNumber(int v)
: number_{v} {}
MyNumber& operator=(const MyNumber&) = delete; // remove assignment
MyNumber(const MyNumber&) = delete; // remove copy
};
int main() {
MyNumber c1{4};
MyNumber c2{7};
c1 = c2; // (ERR) Error – assignment removed by programmer
MyNumber c3{c1}; // (ERR) Error – copy removed by programmer
}
Listing 16.19: With the deleted functions, the compiler prevents incorrect usage of the class.
Book listing lst-0022-book.cpp:
// https://godbolt.org/z/vb5a3EjcY
#include <iostream> // cout
#include "database.hpp" // include the foreign API
class Database {
const db_handle_t db_; // make constant
public:
explicit Database(const char* filename);
~Database();
int execute(const char* query);
Database(const Database&) = delete; // prohibit copying
Database& operator=(const Database&) = delete; // prohibit assignment
};
// … Implementations as before …
int main() {
Database db{ "customers.dat" };
std::cout << "Count: "<< db.execute("select * from cust") << "\n";
Database db2{ db }; // (ERR) compiler prevents dangerous copy
db = db2; // (ERR) compiler prevents dangerous assignment
}
Godbolt Listing lst-0022-godb.cpp, https://godbolt.org/z/vb5a3EjcY:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/vb5a3EjcY
#include <iostream> // cout
#include "database.hpp" // include the foreign API
class Database {
const db_handle_t db_; // make constant
public:
explicit Database(const char* filename);
~Database();
int execute(const char* query);
Database(const Database&) = delete; // prohibit copying
Database& operator=(const Database&) = delete; // prohibit assignment
};
// … Implementations as before …
int main() {
Database db{ "customers.dat" };
std::cout << "Count: "<< db.execute("select * from cust") << "\n";
Database db2{ db }; // (ERR) compiler prevents dangerous copy
db = db2; // (ERR) compiler prevents dangerous assignment
}
Listing 16.20: The class probably contains large amounts of data that are expensive to copy. But what is being copied here?
Book listing lst-0023-book.cpp:
// https://godbolt.org/z/75Kh5Ws45
#include <vector>
class Image {
std::vector<std::byte> data_;
public:
explicit Image(const char *fn) { /*...*/ }
// Compiler generates (among others):
// Copy constructor, assignment, but also moves
};
std::vector<Image> loadCollection(bool empty) {
if(empty) return std::vector<Image>{};
std::vector<Image> result {}; // for return; initially empty
// three images in the collection … copy?
result.push_back( Image{"MonaLisa.png"} );
result.push_back( Image{"TheScream.png"} );
result.push_back( Image{"BoyWithPipe.png"} );
return result; // return collection by value
}
int main() {
// store return in variable
std::vector<Image> collection = loadCollection(false);
}
Godbolt Listing lst-0023-godb.cpp, https://godbolt.org/z/75Kh5Ws45:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/75Kh5Ws45
#include <vector>
class Image {
std::vector<std::byte> data_;
public:
explicit Image(const char *fn) { /*...*/ }
// Compiler generates (among others):
// Copy constructor, assignment, but also moves
};
std::vector<Image> loadCollection(bool empty) {
if(empty) return std::vector<Image>{};
std::vector<Image> result {}; // for return; initially empty
// three images in the collection … copy?
result.push_back( Image{"MonaLisa.png"} );
result.push_back( Image{"TheScream.png"} );
result.push_back( Image{"BoyWithPipe.png"} );
return result; // return collection by value
}
int main() {
// store return in variable
std::vector<Image> collection = loadCollection(false);
}
Listing 16.21: Implementation of the two move operations.
Book listing lst-0024-book.cpp:
// https://godbolt.org/z/aoqW86vs8
#include <vector>
class Image {
std::vector<std::byte> data_; // byte has been available since C++17
public:
explicit Image(const char *fn) { /*...*/ }
Image(Image&& other) noexcept // move constructor
: data_{} // create empty
{
using std::swap;
swap(data_, other.data_);
}
Image& operator=(Image&& other) noexcept { // move operator
using std::swap;
swap(data_, other.data_);
return *this;
}
};
Godbolt Listing lst-0024-godb.cpp, https://godbolt.org/z/aoqW86vs8:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/aoqW86vs8
#include <vector>
class Image {
std::vector<std::byte> data_; // byte has been available since C++17
public:
explicit Image(const char *fn) { /*...*/ }
Image(Image&& other) noexcept // move constructor
: data_{} // create empty
{
using std::swap;
swap(data_, other.data_);
}
Image& operator=(Image&& other) noexcept { // move operator
using std::swap;
swap(data_, other.data_);
return *this;
}
};
Listing 16.22: Infix, function, and method notation for operators.
Book listing lst-0030-book.cpp:
// https://godbolt.org/z/P99563KoP
#include <iostream>
using std::cout; using std::ostream;
struct Widget {
bool operator<(const Widget&) const { // method notation
return true; // always true
}
};
bool operator<(const Widget&, const Widget&) { // function notation
return false; // always false
}
int main() {
Widget x{};
Widget y{};
cout << (operator<(x, y) // calls function notation
? "Method1\n" : "Function1\n");
cout << (y.operator<(x) // calls method notation
? "Method2\n" : "Function2\n");
cout << (x < y // infix notation, allows choice, here function
? "Method3\n" : "Function3\n");
}
Godbolt Listing lst-0030-godb.cpp, https://godbolt.org/z/P99563KoP:
//#(compile) c++; compiler:g141; options:-O2 -std=c++23; libs:-
// https://godbolt.org/z/P99563KoP
#include <iostream>
using std::cout; using std::ostream;
struct Widget {
bool operator<(const Widget&) const { // method notation
return true; // always true
}
};
bool operator<(const Widget&, const Widget&) { // function notation
return false; // always false
}
int main() {
Widget x{};
Widget y{};
cout << (operator<(x, y) // calls function notation
? "Method1\n" : "Function1\n");
cout << (y.operator<(x) // calls method notation
? "Method2\n" : "Function2\n");
cout << (x < y // infix notation, allows choice, here function
? "Method3\n" : "Function3\n");
}
Listing 16.23: A “friend” function is not a method, even if it is inside the class or defined there.
Book listing lst-0031-book.cpp:
// https://godbolt.org/z/Wvr3aK75G
#include <compare> // for <=>
class Value {
int value_;
public:
explicit Value(int value) : value_{value} {}
// free functions, but declared as friend
friend Value operator+(const Value& li, const Value& re);
friend Value operator-(const Value& li, const Value& re)
{ return Value{li.value_ - re.value_}; // also defined inside
}
// operator<=> does not need to be friend.
// for all comparisons: <, >, <=, >= as well as == and !=
auto operator<=>(const Value& re) const = default;
};
// Definition of the previously declared friend function:
Value operator+(const Value& li, const Value& re) {
return Value{li.value_ + re.value_}; // access to private members allowed
}
int main() {
Value seven{7}; Value three{3}; Value two{2};
if((three+two) < seven) { // operator+, then operator< via <=>
return 0; // okay
} else {
return 1; // something went wrong
}
}
Godbolt Listing lst-0031-godb.cpp, https://godbolt.org/z/Wvr3aK75G:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Wvr3aK75G
#include <compare> // for <=>
class Value {
int value_;
public:
explicit Value(int value) : value_{value} {}
// free functions, but declared as friend
friend Value operator+(const Value& li, const Value& re);
friend Value operator-(const Value& li, const Value& re)
{ return Value{li.value_ - re.value_}; // also defined inside
}
// operator<=> does not need to be friend.
// for all comparisons: <, >, <=, >= as well as == and !=
auto operator<=>(const Value& re) const = default;
};
// Definition of the previously declared friend function:
Value operator+(const Value& li, const Value& re) {
return Value{li.value_ + re.value_}; // access to private members allowed
}
int main() {
Value seven{7}; Value three{3}; Value two{2};
if((three+two) < seven) { // operator+, then operator< via <=>
return 0; // okay
} else {
return 1; // something went wrong
}
}
Listing 16.24: A data type almost fully equipped with all operators.
Book listing lst-0032-book.cpp:
// https://godbolt.org/z/YonbGKj8M
#include <iostream> // istream, ostream, cout
class Num {
int val_ = 0;
public:
int& operator*(); // Dereference: Direct access to the value
const int& operator*() const; // Dereference: Read access to the value
Num() {}
explicit Num(int value) : val_{value} {}
// unary operators
Num& operator++(); // Preincrement
Num& operator--(); // Predecrement
Num operator+(); // Positive
Num operator-(); // Negate
Num operator~(); // Bitwise invert
// binary operators
// - compound assignments, arithmetic
Num& operator+=(const Num& re) { val_ += *re; return *this; }
Num& operator-=(const Num& re) { val_ -= *re; return *this; }
Num& operator*=(const Num& re) { val_ *= *re; return *this; }
Num& operator/=(const Num& re) { val_ /= *re; return *this; }
Num& operator%=(const Num& re) { val_ %= *re; return *this; }
// - compound assignments, bitwise
Num& operator|=(const Num& re) { val_ |= *re; return *this; }
Num& operator&=(const Num& re) { val_ &= *re; return *this; }
Num& operator^=(const Num& re) { val_ ^= *re; return *this; }
Num& operator<<=(int n) { val_ <<= n; return *this; }
Num& operator>>=(int n) { val_ >>= n; return *this; }
// - Variation of compound assignments for easier handling
Num& operator+=(int re) { val_ += re; return *this; }
Num& operator-=(int re) { val_ -= re; return *this; }
// binary operators, with call-by-value for the first parameter
// and using compound assignment for assistance
// - Arithmetic
friend Num operator+(Num li, const Num& re) { return li += re; }
friend Num operator-(Num li, const Num& re) { return li -= re; }
friend Num operator*(Num li, const Num& re) { return li *= re; }
friend Num operator/(Num li, const Num& re) { return li /= re; }
friend Num operator%(Num li, const Num& re) { return li %= re; }
// - bitwise
friend Num operator|(Num li, const Num& re) { return li |= re; }
friend Num operator&(Num li, const Num& re) { return li &= re; }
friend Num operator^(Num li, const Num& re) { return li ^= re; }
// - comparisons
// - … fundamental for standard containers and algorithms
friend bool operator==(const Num& li, const Num& re) { return *li == *re; }
auto operator<=>(const Num& re) const {return val_ <=> *re; } // C++20
// - input and output
friend std::ostream& operator<<(std::ostream& os, const Num& arg);
friend std::istream& operator>>(std::istream& is, Num& arg);
};
// unary operators
Num& Num::operator++() { ++val_; return *this; }
Num& Num::operator--() { --val_; return *this; }
Num Num::operator+() { return Num{val_}; }
Num Num::operator-() { return Num{-val_}; }
Num Num::operator~() { return Num{~val_}; }
int& Num::operator*() { return val_; }
const int& Num::operator*() const { return val_; }
// Input and Output
std::ostream& operator<<(std::ostream&os, const Num& arg) { return os<<*arg; }
std::istream& operator>>(std::istream&is, Num& arg) { return is>>*arg; }
Godbolt Listing lst-0032-godb.cpp, https://godbolt.org/z/YonbGKj8M:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/YonbGKj8M
#include <iostream> // istream, ostream, cout
class Num {
int val_ = 0;
public:
int& operator*(); // Dereference: Direct access to the value
const int& operator*() const; // Dereference: Read access to the value
Num() {}
explicit Num(int value) : val_{value} {}
// unary operators
Num& operator++(); // Preincrement
Num& operator--(); // Predecrement
Num operator+(); // Positive
Num operator-(); // Negate
Num operator~(); // Bitwise invert
// binary operators
// - compound assignments, arithmetic
Num& operator+=(const Num& re) { val_ += *re; return *this; }
Num& operator-=(const Num& re) { val_ -= *re; return *this; }
Num& operator*=(const Num& re) { val_ *= *re; return *this; }
Num& operator/=(const Num& re) { val_ /= *re; return *this; }
Num& operator%=(const Num& re) { val_ %= *re; return *this; }
// - compound assignments, bitwise
Num& operator|=(const Num& re) { val_ |= *re; return *this; }
Num& operator&=(const Num& re) { val_ &= *re; return *this; }
Num& operator^=(const Num& re) { val_ ^= *re; return *this; }
Num& operator<<=(int n) { val_ <<= n; return *this; }
Num& operator>>=(int n) { val_ >>= n; return *this; }
// - Variation of compound assignments for easier handling
Num& operator+=(int re) { val_ += re; return *this; }
Num& operator-=(int re) { val_ -= re; return *this; }
// binary operators, with call-by-value for the first parameter
// and using compound assignment for assistance
// - Arithmetic
friend Num operator+(Num li, const Num& re) { return li += re; }
friend Num operator-(Num li, const Num& re) { return li -= re; }
friend Num operator*(Num li, const Num& re) { return li *= re; }
friend Num operator/(Num li, const Num& re) { return li /= re; }
friend Num operator%(Num li, const Num& re) { return li %= re; }
// - bitwise
friend Num operator|(Num li, const Num& re) { return li |= re; }
friend Num operator&(Num li, const Num& re) { return li &= re; }
friend Num operator^(Num li, const Num& re) { return li ^= re; }
// - comparisons
// - … fundamental for standard containers and algorithms
friend bool operator==(const Num& li, const Num& re) { return *li == *re; }
auto operator<=>(const Num& re) const {return val_ <=> *re; } // C++20
// - input and output
friend std::ostream& operator<<(std::ostream& os, const Num& arg);
friend std::istream& operator>>(std::istream& is, Num& arg);
};
// unary operators
Num& Num::operator++() { ++val_; return *this; }
Num& Num::operator--() { --val_; return *this; }
Num Num::operator+() { return Num{val_}; }
Num Num::operator-() { return Num{-val_}; }
Num Num::operator~() { return Num{~val_}; }
int& Num::operator*() { return val_; }
const int& Num::operator*() const { return val_; }
// Input and Output
std::ostream& operator<<(std::ostream&os, const Num& arg) { return os<<*arg; }
std::istream& operator>>(std::istream&is, Num& arg) { return is>>*arg; }
Listing 16.25: You use the data type equipped with operators as usual.
Book listing lst-0033-book.cpp:
// https://godbolt.org/z/YonbGKj8M
#include <iostream> // cout
int main() {
using std::cout;
Num a{1};
*a = 7; // operator* also returns int&
a += Num{3}; // Increment with Num
cout << ( ++( ++a ) ) << "\n"; // Output: 12
a -= 2; // Variation with int
cout << --(--a) << "\n"; // Output: 8
Num b{99};
cout << (a<b ? "yes\n" : "xxx\n"); // Output: yes
cout << (a>b ? "xxx\n" : "no\n"); // Output: no
b /= Num{3}; // b: 33
b %= Num{10}; // b: 3
b <<= 4; // b: 48
b >>= 2; // b: 12
Num c = b / Num{3} + a * Num{2}; // c: 20
}
Godbolt Listing lst-0033-godb.cpp, https://godbolt.org/z/YonbGKj8M:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/YonbGKj8M
#include <iostream> // cout
int main() {
using std::cout;
Num a{1};
*a = 7; // operator* also returns int&
a += Num{3}; // Increment with Num
cout << ( ++( ++a ) ) << "\n"; // Output: 12
a -= 2; // Variation with int
cout << --(--a) << "\n"; // Output: 8
Num b{99};
cout << (a<b ? "yes\n" : "xxx\n"); // Output: yes
cout << (a>b ? "xxx\n" : "no\n"); // Output: no
b /= Num{3}; // b: 33
b %= Num{10}; // b: 3
b <<= 4; // b: 48
b >>= 2; // b: 12
Num c = b / Num{3} + a * Num{2}; // c: 20
}
Listing 16.26: This data type demonstrates the Boolean operators.
Book listing lst-0034-book.cpp:
// https://godbolt.org/z/vjrr6d5TW
#include <iostream> // istream, ostream, cout
class Bool {
bool val_ = false;
bool& operator*() // dereference; mutable
{ return val_; };
const bool& operator*() const // dereference; read-only
{ return val_; }
public:
constexpr Bool() {}
explicit constexpr Bool(bool value)
: val_{value} {}
// unary operators
Bool operator!() const // not operator
{ return Bool{!val_}; };
// binary operators
friend Bool operator&&(const Bool &re, const Bool &li)
{ return Bool{*re && *li}; }
friend Bool operator||(const Bool &re, const Bool &li)
{ return Bool{*re || *li}; }
// input and output
friend std::ostream& operator<<(std::ostream& os, const Bool& arg);
friend std::istream& operator>>(std::istream& is, Bool& arg);
};
std::ostream& operator<<(std::ostream& os, const Bool& arg)
{ return os << *arg; }
std::istream& operator>>(std::istream& is, Bool& arg)
{ return is >> *arg; }
// Constants
static constexpr Bool False{false};
static constexpr Bool True{true};
int main() {
Bool yesno = True && ( Bool{false} || !Bool{} ); // uses &&, ||, and !
std::cout << yesno << "\n"; // Output: 1
}
Godbolt Listing lst-0034-godb.cpp, https://godbolt.org/z/vjrr6d5TW:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/vjrr6d5TW
#include <iostream> // istream, ostream, cout
class Bool {
bool val_ = false;
bool& operator*() // dereference; mutable
{ return val_; };
const bool& operator*() const // dereference; read-only
{ return val_; }
public:
constexpr Bool() {}
explicit constexpr Bool(bool value)
: val_{value} {}
// unary operators
Bool operator!() const // not operator
{ return Bool{!val_}; };
// binary operators
friend Bool operator&&(const Bool &re, const Bool &li)
{ return Bool{*re && *li}; }
friend Bool operator||(const Bool &re, const Bool &li)
{ return Bool{*re || *li}; }
// input and output
friend std::ostream& operator<<(std::ostream& os, const Bool& arg);
friend std::istream& operator>>(std::istream& is, Bool& arg);
};
std::ostream& operator<<(std::ostream& os, const Bool& arg)
{ return os << *arg; }
std::istream& operator>>(std::istream& is, Bool& arg)
{ return is >> *arg; }
// Constants
static constexpr Bool False{false};
static constexpr Bool True{true};
int main() {
Bool yesno = True && ( Bool{false} || !Bool{} ); // uses &&, ||, and !
std::cout << yesno << "\n"; // Output: 1
}
Listing 16.27: The comparison categories contain constants for the return.
Book listing lst-0036-book.cpp:
// https://godbolt.org/z/KxWb4q8q9
#include <compare> // partial_ordering etc
#include <set>
#include <iostream>
using namespace std;
struct Fraction {
int z, n; // numerator / denominator
partial_ordering operator<=>(const Fraction& rhs) const {
if(n==0 || rhs.n==0) return partial_ordering::unordered;
return (double)z / n <=> (double)rhs.z / rhs.n;
}
};
int main() {
set<Fraction> fractions{ {1,2}, {2,4}, {1,3}, {2,3}, {1,4}, {2,5}, {3,8}, {99,0} };
for(auto f : fractions)
cout << f.z << "/" << f.n << " ";
cout << "\n”; // Output: 1/4 1/3 3/8 2/5 1/2 2/3
}
Godbolt Listing lst-0036-godb.cpp, https://godbolt.org/z/KxWb4q8q9:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/KxWb4q8q9
#include <compare> // partial_ordering etc
#include <set>
#include <iostream>
using namespace std;
struct Fraction {
int z, n; // numerator / denominator
partial_ordering operator<=>(const Fraction& rhs) const {
if(n==0 || rhs.n==0) return partial_ordering::unordered;
return (double)z / n <=> (double)rhs.z / rhs.n;
}
};
int main() {
set<Fraction> fractions{ {1,2}, {2,4}, {1,3}, {2,3}, {1,4}, {2,5}, {3,8}, {99,0} };
for(auto f : fractions)
cout << f.z << "/" << f.n << " ";
cout << "\n”; // Output: 1/4 1/3 3/8 2/5 1/2 2/3
}
Listing 16.28: You can use this helper function to implement a simple ordered comparison.
Book listing lst-0038-book.cpp:
auto operator<=>(const Address& re) const {
array a{street, zip, city};
array b{re.street, re.zip, re.city};
return lexicographical_compare_three_way(begin(a), end(a), begin(b), end(b));
}
Listing 16.29: A virtual method that is = 0 is called pure virtual or abstract.
Book listing lst-0039-book.cpp:
// https://godbolt.org/z/1zq4jfdYb
#include <string>
#include <iostream>
using std::string; using std::ostream;
class Shape {
string color_;
public:
virtual double calcArea() const = 0; // pure virtual method
string getColor() const { return color_; }
void setColor(const string& color) { color_ = color; }
virtual ~Shape() {}
};
class Square : public Shape {
double len_;
public:
explicit Square(double len) : len_{len} {}
double calcArea() const override { return len_*len_; }
};
class Circle : public Shape {
double rad_;
public:
explicit Circle(double rad) : rad_{rad} {}
double calcArea() const override { return 3.1415*rad_*rad_; }
};
struct Calculator {
Shape& shape_;
Calculator(Shape& shape) : shape_{shape} { }
void run(ostream& os) const {
os << "The area of the shape is " << shape_.calcArea() << "\n";
}
};
int main() {
Square square {5.0};
Calculator ti { square };
ti.run(std::cout); // Output: The area of the shape is 25
}
Godbolt Listing lst-0039-godb.cpp, https://godbolt.org/z/1zq4jfdYb:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/1zq4jfdYb
#include <string>
#include <iostream>
using std::string; using std::ostream;
class Shape {
string color_;
public:
virtual double calcArea() const = 0; // pure virtual method
string getColor() const { return color_; }
void setColor(const string& color) { color_ = color; }
virtual ~Shape() {}
};
class Square : public Shape {
double len_;
public:
explicit Square(double len) : len_{len} {}
double calcArea() const override { return len_*len_; }
};
class Circle : public Shape {
double rad_;
public:
explicit Circle(double rad) : rad_{rad} {}
double calcArea() const override { return 3.1415*rad_*rad_; }
};
struct Calculator {
Shape& shape_;
Calculator(Shape& shape) : shape_{shape} { }
void run(ostream& os) const {
os << "The area of the shape is " << shape_.calcArea() << "\n";
}
};
int main() {
Square square {5.0};
Calculator ti { square };
ti.run(std::cout); // Output: The area of the shape is 25
}
Listing 16.30: With an “enum”, you define a type with its own value range.
Book listing lst-0044-book.cpp:
// https://godbolt.org/z/xhd7s1YhE
#include <string>
using std::string;
enum class StoplightColor {
RED, YELLOW, REDYELLOW, GREEN
};
struct Stoplight {
StoplightColor color_;
Stoplight(StoplightColor color, string name) : color_{color} {}
};
Stoplight createStoplight(StoplightColor color, string name) {
return Stoplight{color, name};
}
int main() {
Stoplight stoplight = createStoplight(StoplightColor::RED, "AX-001");
}
Godbolt Listing lst-0044-godb.cpp, https://godbolt.org/z/xhd7s1YhE:
//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/xhd7s1YhE
#include <string>
using std::string;
enum class StoplightColor {
RED, YELLOW, REDYELLOW, GREEN
};
struct Stoplight {
StoplightColor color_;
Stoplight(StoplightColor color, string name) : color_{color} {}
};
Stoplight createStoplight(StoplightColor color, string name) {
return Stoplight{color, name};
}
int main() {
Stoplight stoplight = createStoplight(StoplightColor::RED, "AX-001");
}
GodboltId:f8on7bs1Y
Book listing lst-0046-book.cpp:
// https://godbolt.org/z/f8on7bs1Y
string drivingSchool(Stoplight light) {
switch(light.color) {
using enum StoplightColor; // "StoplightColor::" can now be omitted
case RED: return "stop";
case REDYELLOW: return "get ready";
case YELLOW: return "slow down";
case GREEN: return "go";
}
abort(); // exhaustive switch, should not happen
}
Godbolt Listing lst-0046-godb.cpp, https://godbolt.org/z/f8on7bs1Y:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/f8on7bs1Y
string drivingSchool(Stoplight light) {
switch(light.color) {
using enum StoplightColor; // "StoplightColor::" can now be omitted
case RED: return "stop";
case REDYELLOW: return "get ready";
case YELLOW: return "slow down";
case GREEN: return "go";
}
abort(); // exhaustive switch, should not happen
}
Listing 17.31: In an “enum”, you can also specify the desired numerical values.
Book listing lst-0047-book.cpp:
// https://godbolt.org/z/Yjbzrrnob
enum class Weekday {
MO=1, TU, WE, TH, FR, SA, SU // TU becomes 2, WE becomes 3, etc.
};
enum class Level {
TRACE=1, DEBUG, INFO=10, ERROR, FATAL // gaps are also possible
};
void log(Level level) {
int intLevel = (int)level; // explicitly cast to an int
if(intLevel > 10) { /* ... */ }
}
Godbolt Listing lst-0047-godb.cpp, https://godbolt.org/z/Yjbzrrnob:
//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/Yjbzrrnob
enum class Weekday {
MO=1, TU, WE, TH, FR, SA, SU // TU becomes 2, WE becomes 3, etc.
};
enum class Level {
TRACE=1, DEBUG, INFO=10, ERROR, FATAL // gaps are also possible
};
void log(Level level) {
int intLevel = (int)level; // explicitly cast to an int
if(intLevel > 10) { /* ... */ }
}