Session 3#
Agenda#
C++ Functors and Lambdas
C++ Make and CMake
Excercises
C++ Functors and Lambdas#
`c++ -std=c++17 -o functors_and_lamda_functions functors_and_lamda_functions.cpp
1/*
2 Functors and Lambda Functions
3
4 Useful References:
5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
6 * https://en.cppreference.com/
7*/
8
9#include <iostream> // standard IO
10#include <vector> // standard "vector" library for dynamic arrays
11#include <algorithm> // for the transform functions
12
13
14/*
15
16A functor is essentially a class that has defined the "()" operator.
17
18This lets us define objects that behave like functions.
19
20There are 2 main reasons we might want a function like object:
21
22 1. function like object allow us to define, use, and modify
23 properties, for instance the current state of an object.
24 In some applications this may be valueable and simplify
25 the code (e.g. the function "knows" informatin that you
26 would otherwise have to pass to the function.) This also
27 allows you to initialize frequently used values once,
28 which is especially useful if they're expensive to compute.
29
30 2. By using a functor, the compiler is often able to do a
31 better job optimizing. Essentially this happens because
32 the compiler knows exactly what function is being called,
33 whereas if you pass a pointer to a function, the compiler
34 just knows a function is being called (but not necessarily
35 which one.) This means, for example, the compiler might
36 inline a functor.
37
38*/
39
40// example of a simple functor
41// You see functors defined as structs instead of classes, classes are
42// more general structure with extra features (that we're not using)
43class increment
44{
45private:
46 // define a private member variable to hold the amount we will
47 // increment by
48 // It is common practice to prepend member variables with "m_"
49 // but this is purely a style choice.
50 int m_increment_amount;
51
52public:
53
54 // constructor. This sets the default values.
55 // This needs to be the same name as the class
56 // Note we can overload the constructor. Here,
57 // we're setting the increment amount to 1 if we're
58 // not provided an argument or we're setting it
59 // to the inputed value.
60 increment() : m_increment_amount(1){};
61 increment(const int &n) : m_increment_amount(n){};
62
63 // Now we're going to define the "()" operator
64 // so we can increment the input and return the
65 // new value
66 int operator()(const int& input_num) const
67 {
68 // add the increment amount to the inputed value
69 // and return that new value
70 return m_increment_amount + input_num;
71 }
72
73 void changeIncrementAmount(const int &inc) {
74 m_increment_amount = inc;
75 }
76}; // end of increment class. Note, ends with ;
77
78
79// function to print vectors
80void print_vector(const std::vector<int>& v)
81{
82 for (const auto& val: v) {
83 std::cout << val << ", ";
84 }
85 std::cout << std::endl;
86}
87
88
89int main() {
90
91 // create an array of values to increment
92 std::vector<int> v = {1, 2, 3, 4, 5}; // list initialized requires c++11 or later
93
94 // use our increment functor to add 1
95 increment inc_by_one; // initialize the functor with the default
96 for (auto &val : v) {
97 val = inc_by_one(val);
98 }
99 print_vector(v);
100
101 // use our increment functor to subtract 1
102 increment dec_by_one(-1); // initialize the functor to subtract one
103 for (auto &val : v)
104 {
105 val = dec_by_one(val);
106 }
107 print_vector(v);
108
109 // use our increment functor to add 2 to each value using the
110 // std::transform function
111 //
112 // There is a subtlety here. The increment(2) argument here is
113 // calling the constructor and setting the increment value.
114 // Inside the transform function, it is calling v[i] = incremnt(v[i])
115 std::transform(v.begin(), v.end(), v.begin(), increment(2));
116 print_vector(v);
117
118 // subtract 2 from each entry
119 std::transform(v.begin(), v.end(), v.begin(), increment(-2));
120 print_vector(v);
121
122 // The functor we defined above is a lot of work for something that
123 // is pretty trivial (and might be even more work if the function
124 // object needed to be more complex).
125 //
126 // Since C++11, C++ has "lambda expressions," which is basically
127 // a way for the compiler to generate a functor for you using some
128 // shorthand notation
129
130 // Make a lambda function that adds 1
131 // auto requires c++14
132 // the [&] specifies we should capture values by reference
133 // we could also capture by value, pointer, etc.
134 // Then the inputs are in (...), we're taking an int
135 // Finally, we define the function in {...} (just returning input + 1)
136 auto add_one_lambda = [&](const int& val){return val + 1;};
137 std::transform(v.begin(), v.end(), v.begin(), add_one_lambda);
138 print_vector(v);
139
140 // subtract 1 using lambdas
141 auto dec_one_lambda = [&](const int &val){ return val - 1; };
142 std::transform(v.begin(), v.end(), v.begin(), dec_one_lambda);
143 print_vector(v);
144
145 // We can also use lambda functions inline. Here, we'll add 2
146 // Again, for style preferences, I find it easier to read lambda expressions
147 // if they're defined like the above. This example is inline and extactly the
148 // same, but more complicated expressions may be difficult to read.
149 std::transform(v.begin(), v.end(), v.begin(), [&](const int &val){ return val + 2; });
150 print_vector(v);
151
152 // and subtract 3
153 std::transform(v.begin(), v.end(), v.begin(), [&](const int &val){ return val - 3; });
154 print_vector(v);
155}
C++ Make and CMake#
Excercises#
Problem 2 from Project Euler.