
- •Table of Contents
- •About the Author
- •About the Technical Reviewer
- •Acknowledgments
- •Software Entropy
- •Clean Code
- •C++11: The Beginning of a New Era
- •Who This Book Is For
- •Conventions Used in This Book
- •Sidebars
- •Notes, Tips, and Warnings
- •Code Samples
- •Coding Style
- •C++ Core Guidelines
- •Companion Website and Source Code Repository
- •UML Diagrams
- •The Need for Testing
- •Unit Tests
- •What About QA?
- •Rules for Good Unit Tests
- •Test Code Quality
- •Unit Test Naming
- •Unit Test Independence
- •One Assertion per Test
- •Independent Initialization of Unit Test Environments
- •Exclude Getters and Setters
- •Exclude Third-Party Code
- •Exclude External Systems
- •What Do We Do with the Database?
- •Don’t Mix Test Code with Production Code
- •Tests Must Run Fast
- •How Do You Find a Test’s Input Data?
- •Equivalence Partitioning
- •Boundary Value Analysis
- •Test Doubles (Fake Objects)
- •What Is a Principle?
- •KISS
- •YAGNI
- •It’s About Knowledge!
- •Building Abstractions Is Sometimes Hard
- •Information Hiding
- •Strong Cohesion
- •Loose Coupling
- •Be Careful with Optimizations
- •Principle of Least Astonishment (PLA)
- •The Boy Scout Rule
- •Collective Code Ownership
- •Good Names
- •Names Should Be Self-Explanatory
- •Use Names from the Domain
- •Choose Names at an Appropriate Level of Abstraction
- •Avoid Redundancy When Choosing a Name
- •Avoid Cryptic Abbreviations
- •Avoid Hungarian Notation and Prefixes
- •Avoid Using the Same Name for Different Purposes
- •Comments
- •Let the Code Tell the Story
- •Do Not Comment Obvious Things
- •Don’t Disable Code with Comments
- •Don’t Write Block Comments
- •Don’t Use Comments to Substitute Version Control
- •The Rare Cases Where Comments Are Useful
- •Documentation Generation from Source Code
- •Functions
- •One Thing, No More!
- •Let Them Be Small
- •“But the Call Time Overhead!”
- •Function Naming
- •Use Intention-Revealing Names
- •Parameters and Return Values
- •Avoid Flag Parameters
- •Avoid Output Parameters
- •Don’t Pass or Return 0 (NULL, nullptr)
- •Strategies for Avoiding Regular Pointers
- •Choose simple object construction on the stack instead of on the heap
- •In a function’s argument list, use (const) references instead of pointers
- •If it is inevitable to deal with a pointer to a resource, use a smart one
- •If an API returns a raw pointer...
- •The Power of const Correctness
- •About Old C-Style in C++ Projects
- •Choose C++ Strings and Streams over Old C-Style char*
- •Use C++ Casts Instead of Old C-Style Casts
- •Avoid Macros
- •Managing Resources
- •Resource Acquisition Is Initialization (RAII)
- •Smart Pointers
- •Unique Ownership with std::unique_ptr<T>
- •Shared Ownership with std::shared_ptr<T>
- •No Ownership, but Secure Access with std::weak_ptr<T>
- •Atomic Smart Pointers
- •Avoid Explicit New and Delete
- •Managing Proprietary Resources
- •We Like to Move It
- •What Are Move Semantics?
- •The Matter with Those lvalues and rvalues
- •rvalue References
- •Don’t Enforce Move Everywhere
- •The Rule of Zero
- •The Compiler Is Your Colleague
- •Automatic Type Deduction
- •Computations During Compile Time
- •Variable Templates
- •Don’t Allow Undefined Behavior
- •Type-Rich Programming
- •Know Your Libraries
- •Take Advantage of <algorithm>
- •Easier Parallelization of Algorithms Since C++17
- •Sorting and Output of a Container
- •More Convenience with Ranges
- •Non-Owning Ranges with Views
- •Comparing Two Sequences
- •Take Advantage of Boost
- •More Libraries That You Should Know About
- •Proper Exception and Error Handling
- •Prevention Is Better Than Aftercare
- •No Exception Safety
- •Basic Exception Safety
- •Strong Exception Safety
- •The No-Throw Guarantee
- •An Exception Is an Exception, Literally!
- •If You Can’t Recover, Get Out Quickly
- •Define User-Specific Exception Types
- •Throw by Value, Catch by const Reference
- •Pay Attention to the Correct Order of Catch Clauses
- •Interface Design
- •Attributes
- •noreturn (since C++11)
- •deprecated (since C++14)
- •nodiscard (since C++17)
- •maybe_unused (since C++17)
- •Concepts: Requirements for Template Arguments
- •The Basics of Modularization
- •Criteria for Finding Modules
- •Focus on the Domain of Your Software
- •Abstraction
- •Choose a Hierarchical Decomposition
- •Single Responsibility Principle (SRP)
- •Single Level of Abstraction (SLA)
- •The Whole Enchilada
- •Object-Orientation
- •Object-Oriented Thinking
- •Principles for Good Class Design
- •Keep Classes Small
- •Open-Closed Principle (OCP)
- •A Short Comparison of Type Erasure Techniques
- •Liskov Substitution Principle (LSP)
- •The Square-Rectangle Dilemma
- •Favor Composition over Inheritance
- •Interface Segregation Principle (ISP)
- •Acyclic Dependency Principle
- •Dependency Inversion Principle (DIP)
- •Don’t Talk to Strangers (The Law of Demeter)
- •Avoid Anemic Classes
- •Tell, Don’t Ask!
- •Avoid Static Class Members
- •Modules
- •The Drawbacks of #include
- •Three Options for Using Modules
- •Include Translation
- •Header Importation
- •Module Importation
- •Separating Interface and Implementation
- •The Impact of Modules
- •What Is Functional Programming?
- •What Is a Function?
- •Pure vs Impure Functions
- •Functional Programming in Modern C++
- •Functional Programming with C++ Templates
- •Function-Like Objects (Functors)
- •Generator
- •Unary Function
- •Predicate
- •Binary Functors
- •Binders and Function Wrappers
- •Lambda Expressions
- •Generic Lambda Expressions (C++14)
- •Lambda Templates (C++20)
- •Higher-Order Functions
- •Map, Filter, and Reduce
- •Filter
- •Reduce (Fold)
- •Fold Expressions in C++17
- •Pipelining with Range Adaptors (C++20)
- •Clean Code in Functional Programming
- •The Drawbacks of Plain Old Unit Testing (POUT)
- •Test-Driven Development as a Game Changer
- •The Workflow of TDD
- •TDD by Example: The Roman Numerals Code Kata
- •Preparations
- •The First Test
- •The Second Test
- •The Third Test and the Tidying Afterward
- •More Sophisticated Tests with a Custom Assertion
- •It’s Time to Clean Up Again
- •Approaching the Finish Line
- •Done!
- •The Advantages of TDD
- •When We Should Not Use TDD
- •TDD Is Not a Replacement for Code Reviews
- •Design Principles vs Design Patterns
- •Some Patterns and When to Use Them
- •Dependency Injection (DI)
- •The Singleton Anti-Pattern
- •Dependency Injection to the Rescue
- •Adapter
- •Strategy
- •Command
- •Command Processor
- •Composite
- •Observer
- •Factories
- •Simple Factory
- •Facade
- •The Money Class
- •Special Case Object (Null Object)
- •What Is an Idiom?
- •Some Useful C++ Idioms
- •The Power of Immutability
- •Substitution Failure Is Not an Error (SFINAE)
- •The Copy-and-Swap Idiom
- •Pointer to Implementation (PIMPL)
- •Structural Modeling
- •Component
- •Interface
- •Association
- •Generalization
- •Dependency
- •Template and Template Binding
- •Behavioral Modeling
- •Activity Diagram
- •Action
- •Control Flow Edge
- •Other Activity Nodes
- •Sequence Diagram
- •Lifeline
- •Message
- •State Diagram
- •State
- •Transitions
- •External Transitions
- •Internal Transitions
- •Trigger
- •Stereotypes
- •Bibliography
- •Index

Chapter 7 Functional Programming
Note Until C++11, it was a good practice that functors, depending on their number of parameters, were derived from the templates std::unary_function and std::binary_function (both defined in the <functional> header).
These templates have been labeled as deprecated with C++11 and have been removed from the Standard Library since C++17.
Binders and Function Wrappers
The next development step in terms of functional programming in C++ was made with the publication of the draft C++ Technical Report 1 (TR 1) in 2005, which is the common name for the standard ISO/IEC TR 19768:2007 C++ Library Extensions. The TR 1 specifies a series of extensions to the C++ Standard Library, including, among other things, extensions for functional programming. This technical report was the library extension proposal for the later C++11 standard, and in fact, 12 of the 13 proposed libraries (with slight modifications) also made it into the new language standard published in 2011.
In terms of functional programming, the TR 1 introduced the two function templates std::bind and std::function, which are defined in the <functional> library header.
The function template std::bind is a binder wrapper for functions and their arguments. You can take a function (or a function pointer, or a functor), and “bind” actual values to one or all of the function’s parameters. In other words, you can create new function-like objects from existing functions or functors. Let’s start with a simple example, as shown in Listing 7-17.
Listing 7-17. Using std::bind to Wrap the multiply() Binary Function
#include <functional> #include <iostream>
[[nodiscard]] constexpr double multiply(const double multiplicand, const double multiplier) noexcept {
return multiplicand * multiplier;
}
int main() {
const auto result1 = multiply(10.0, 5.0);
312
Chapter 7 Functional Programming
auto boundMultiplyFunctor = std::bind(multiply, 10.0, 5.0); const auto result2 = boundMultiplyFunctor();
std::cout << "result1 = " << result1 << ", result2 = " << result2 << std::endl;
return 0;
}
In this example, the multiply() function is wrapped, together with two floating- point number literals (10.0 and 5.0), using std::bind. The number literals represent the actual parameters that are bound to the two function arguments multiplicand and multiplier. As a result, we get a new function-like object that is stored in the boundMultiplyFunctor variable. It can then be called like an ordinary functor using the parenthesis operator.
Maybe you are wondering, nice, but I don’t get it. What’s the purpose of that? What is the practical benefit of the binder function template?
Well, std::bind allows something that is known as partial application (or partial function application) in programming. Partial application is a process by which only a subset of the function parameters is bound to values or variables, whereas the other part is not yet bound. The unbound parameters are replaced with the placeholders _1, _2, _3, and so on, which are defined in the namespace std::placeholders. See Listing 7-18.
Listing 7-18. An Example of Partial Function Application
#include <functional> #include <iostream>
[[nodiscard]] constexpr double multiply(const double multiplicand, const double multiplier) noexcept {
return multiplicand * multiplier;
}
int main() {
using namespace std::placeholders;
auto multiplyWith10 = std::bind(multiply, _1, 10.0); std::cout << "result = " << multiplyWith10(5.0) << std::endl; return 0;
}
313
Chapter 7 Functional Programming
In this example, the second parameter of the multiply() function is bound to the floating-point number literal 10.0, but the first parameter is bound to a placeholder. The function-like object, which is the return value of std::bind(), is stored in the multiplyWith10 variable. This variable can now be used like a function, but we only need to pass one parameter: the value that is to be multiplied by 10.0.
Partial function application is an adaptation technique that allows us to use a function or a functor in various situations, when we need their functionality, but when we can supply some but not all of the arguments. In addition, with the help of the placeholders, the order of the functions parameters can be adapted to the order that the client code expects. For example, the position of the multiplicand and the multiplier in the parameter list can be interchanged by mapping them to a new function-like object in the following way:
auto multiplyWithExchangedParameterPosition = std::bind(multiply, _2, _1);
In our case with the multiply() function, this is obviously senseless (remember the commutative property of multiplication), because the new function object will produce the same results as the original multiply() function. However, in other situations, adapting the order of the parameters can improve the usability of a function. Partial function application is a tool for interface adaptation.
By the way, especially in conjunction with functions as return parameters, the automatic type deduction with its keyword auto (see the section entitled “Automatic Type Deduction” in Chapter 5) can provide valuable services, because if we inspect what the GCC compiler returns from the call to std::bind(), it is an object of the following complex type:
std::_Bind_helper<bool0,double (&)(double, double),const _Placeholder<int2> &,const _Placeholder<int1> &>::type
Terrifying, isn’t it? Writing down such a type explicitly in source code is not only less helpful, but apart from that the readability of the code also suffers considerably. Thanks to the keyword auto, it is not necessary to define these types explicitly. But in those rare cases, where you must do it, the class template std::function comes into play, which is a general-purpose polymorphic function wrapper. This template can wrap an arbitrary callable object (an ordinary function, a functor, a function pointer, etc.) and manages the memory used to store that object. For example, to wrap our multiplication function multiply() into a std::function object, the code looks as follows:
314

Chapter 7 Functional Programming
std::function<double(double, double)> multiplyFunc = multiply; auto result = multiplyFunc(10.0, 5.0);
Now that we’ve discussed std::bind, std::function, and the technique of partial application, I have a possibly disappointing message for you: since C++11 and the introduction of lambda expressions, most of this template stuff from the C++ Standard Library is only seldom required.
Lambda Expressions
With the advent of C++11, the language has been extended with a new and noteworthy feature: lambda expressions! Other frequently used terms for them are lambda functions, function literals, or just lambdas. Sometimes they are also called closures, which is actually a general term from functional programming, and which, incidentally, is also not entirely correct.
CLOSURE
In imperative programming languages, we are accustomed to the fact that a variable is no longer available when the program execution leaves the scope within which the variable is defined. For instance, if a function is done and returns to its caller, all local variables of that function are removed from the call stack and deleted from memory.
On the other hand, in functional programming, we can build a closure, which is a function object with a persistent local variable scope. In other words, closures allow a scope with some or all of its local variables to be tied to a function, and this scope object will persist as long as that function exists.
In C++, such closures can be created with the help of lambda expressions due to the capture list in the lambda introducer. A closure is not the same as a lambda expression, just like an object (instance) in object orientation is not the same as its class.
315
Chapter 7 Functional Programming
What is special about lambda expressions is that they are usually implemented inline, that is, at the point of their application. This can sometimes improve the readability of the code, and compilers can apply their optimization strategies even more efficiently. Of course, lambda functions can also be treated as data, for example, stored in variables or passed as a function argument to a so-called high-order function (see the next section about this topic).
The basic structure of a lambda expression looks as follows:
[ capture list ](parameter list) -> return_type_declaration { lambda body }
Since this book is not a C++ language introduction, I will not explain all the basics of lambda expressions here. Even if you are seeing something like this for the first time, it should be relatively clear that the return type, the parameter list, and the lambda body are pretty much the same as with ordinary functions. What might seem unusual at first glance are two things. For example, a lambda expression has no name like an ordinary function or a function-like object. This is the reason that one speaks in this context of anonymous functions. The other conspicuousness is the square bracket at the beginning, which is also called the lambda introducer. As the name suggests, the lambda introducer marks the beginning of a lambda expression. In addition, the introducer also optionally contains something called a capture list.
What makes this capture list so important is that all the variables from the outside scope are listed, which should be available inside of the lambda body, and whether they should be captured by value (copying) or by reference. In other words, these are the closures of the lambda expression.
An example lambda expression is defined as follows:
[](const double multiplicand, const double multiplier) { return multiplicand * multiplier; }
This is our good old multiplication function from the previous section as a lambda. The introducer has a blank capture list, which means that nothing from the surrounding scope is used. The return type is not specified in this case either, because the compiler can easily deduce it.
By assigning the lambda expression to a variable, a corresponding runtime object is created, the so-called closure. And this is actually true: the compiler generates a functor class of an unspecified type from a lambda expression, which is instantiated at runtime and assigned to the variable. The captures in the capture list are converted into
316
Chapter 7 Functional Programming
constructor parameters and member variables of the functor object. The parameters in the lambda’s parameter list are turned into parameters for the functor’s parenthesis operator (operator()). See Listing 7-19.
Listing 7-19. Using the Lambda Expression to Multiply Two Doubles
#include <iostream>
int main() {
auto multiply = [](const double multiplicand, const double multiplier) { return multiplicand * multiplier;
};
std::cout << multiply(10.0, 50.0) << std::endl; return 0;
}
However, the whole thing can be shorter, because a lambda expression can be called directly at the place of its definition by appending parentheses with arguments behind the lambda body. See Listing 7-20.
Listing 7-20. Defining and Calling a Lambda Expression in One Go
int main() { std::cout <<
[](const double multiplicand, const double multiplier) { return multiplicand * multiplier;
}(50.0, 10.0) << std::endl; return 0;
}
The previous example is, of course, for demonstration purposes only, since the use of a lambda in this style makes no sense. The example in Listing 7-21 uses two lambda expressions. One is used by the algorithm called std::transform to envelop the words in the string vector called quote with angle brackets and store them in another vector named result. The other lambda expression is used by std::for_each to output the content of result on standard output.
317