Summary

We start this session by giving an introduction to the auto keyword, before getting into some of the more complex C++ features: lambda functions, function pointers, functors, polymorphic function wrappers and pointers to member functions. Finally, we end the session by a gentle introduction to C++ multithreading.

The auto keyword

C++11 introduced the auto keyword that actually specifies that the type of the declared variable will automatically being deduced from its initializer. This allows us to do things such as:

auto a = 5;
std::cout << "type of a: " << typeid(a).name() << std::endl; // type of a: i
auto b = 4.2;
std::cout << "type of b: " << typeid(c).name() << std::endl; // type of b: d
auto c = a + b;
std::cout << "type of c: " << typeid(b).name() << std::endl; // type of c: d

Or use it conveniently in, among other places, loops and/or function parameters:

#include <iostream>
#include <vector>

void foo(auto& v, double factor) {
  for (auto it = v.begin(); it != v.end(); ++it) {
    *it = *it * factor;
  }
}

int main() {
  std::vector<double> v = {1.2, 3.4, 5.7};
  std::cout << foo(v, 0.5) << std::endl;

  return 0;
}

Since C++14 however, you can use auto as the type specifier for the return type:

#include <iostream>

auto foo(int x) {
  int y = 6;
  return x + y;
}

int main() {
  std::cout << foo(3) << endl;
  return 0;
}

Of course, it is debatable that all of your code stays readable... but hey, it saves you typing time!

Note that this is a simple and not so useful example of using auto as a return type. C++14 however allows some pretty neat and advanced stuff with auto like generalizing the return type by depending on the types of the parameter arguments. (Actually, this was also already available in C++11, but than you had to use the trailing return type syntax.) When you want to do such advanced things correctly, you'll probably also have to look at the decltype (especially when there are references involved).

You should typically only use auto when the type would otherwise be repeated, when the type can easily be deduced (such as the iterator of a vector) or when you really need it for advanced constructions with templates (which we will see next session).

Look for more information and examples on:

Lambda functions

Also called anonymous functions is a feature that provides support for defining "nameless" functions on-the-spot; usually used as wrappers or short operations to be performed on loops etc. Their main use is to reduce notational clutter in situations where one needs a function (or functor, as seen later this session) but does not want to litter the code with declarations that would be used just once:

// Using a regular function
double sum1 (double x, double y) {
    return x + y;
}
std::cout << sum1(3.14, 15.82) << std::endl;

// Using a lambda function
auto sum2 = [](double x, double y){ return x + y };
std::cout << sum2(3.14, 15.82) << std::endl;

// Or in one line
std::cout << [](double x, double y){ return x + y }(3.14, 15.82) << std::endl;

As you can see, lambda functions can even be defined and called as r-values, without having to give them a name. This is actually the way lambda functions are usually used. Although many things can be omitted, the general syntax of lambda functions is:

[capture](arguments) -> return_type {/* body */}

Another simple example would be:

#include <iostream>

int main() {
    auto f = []() -> void {
        std::cout << "Greetings from lambdaland!" << std::endl;
    };

    f();

    return 0;
}

In many cases the return type can be omitted:

auto f = []() {
    cout << "Greetings from lambdaland!" << endl;
};

Capture

The above lambda function has no capture: it has no access to variables outside its scope. Suppose we'd want to do this:

const double pi = 3.14159265;
auto f = []() {
    std::cout << "I want pi! " << pi << std::endl;
};

f();

Computer says NO:

error: 'pi' is not captured
     std::cout << "I want pi! " << pi << std::endl;
                                   ^

Capture all by value

To give the lambda access to its "surrounding" scope we can set the default capture to by value using [=]:

const double pi = 3.14159265;
auto f = [=]() {
    std::cout << "I want pi! " << pi << std::endl;
};

f();

In which case the lambda accesses a local copy of pi (and any other variables that are used in the lambda). No changes to that local copy are permitted: you will get messages like error: assignment of read-only variable 'pi'.

Capture all by reference

If you want to be able to change the values of the captured variables you need to capture by reference using [&]:

string msg = "Hello";
auto f = [&]() {
    msg += " from lambda!";
};

std::cout << msg << std::endl;
f();
std::cout << msg << std::endl;

which of course gives:

Hello
Hello from lambda!

Explicit capture

If you need more fine-grained control over what gets captured and how, you can explicitly name the captured variables:

const double pi = 3.14159265;
string msg = "Hello";

auto f = [&msg, pi]() {
    cout << "I want pi! " << pi << endl;
    msg += " from lambda!";
};

cout << msg << endl;
f();
cout << msg << endl;

In such cases, accessing anything other than msg and pi within the lambda is an error. Also note that msg will be captured by reference here and pi by value.

When using the lambda capture, keep in mind some of its pitfalls, such as dangling references and dangling pointers.

Type of lambda expressions

In case you might wonder what hides behind the auto type we're using in the previous examples you can try the following little experiment:

int main() {
    const double pi = 3.14159265;
    std::string msg = "Hello";

    auto f = [&]() {
        msg += " from lambda!";
    };

    auto g = [=]() {
        std::cout << "I want pi! " << pi << std::endl;
    };

    std::cout << typeid(f).name() << std::endl;
    std::cout << typeid(g).name() << std::endl;

    return 0;
}

Compiling with GCC (7.3) gives:

Z4mainEUlvE_
Z4mainEUlvE0_

These are mangled symbol names. To decode (unmangle) them, use the c++filt command line tool: ./my_program | c++filt -t:

main::{lambda()#1}
main::{lambda()#2}

Symbol names of lambda expressions are not defined by the standard and are thus compiler-dependent. For instance, on Clang I get: main::$_0 and main::$_1.

Notice that even though they share the same signature (capture doesn't play a role here, you can try if you want) the two lambdas have a different type. ALL lambdas have their own unique type.

Because of the above, C++ does not allow you to define a vector of lambdas. Instead, you can use a polymorphic function wrapper, which will be a later topic in this session.

Lambdas and the std algorithms

In many cases, for instance sorting with std::sort, you can define your own "comparators" to alter the default behaviour of std algorithms or to allow the algorithms to run on user-defined types. For example, the predefined operator< for std::string results in alphabetical sorting. If you would like to use some other way to sort elements, you can pass your own string comparator as a lambda function to the std::sort routine:

std::vector <std::string> v;
v.push_back("Lorem");
v.push_back("ipsum");
v.push_back("dolor");
v.push_back("sit");
v.push_back("amet");

// Sort vector by string length
std::sort(v.begin(), v.end(), [](std::string a, std::string b)->bool {
    return a.length() < b.length();
});

for (std::string s : v) {
    std::cout << s << std::endl;
}

Before C++11 you'd need to define a separate function or functor to achieve this.

Reading material

Functions, C++ style

Working with classes and objects in C++ does not mean we can throw away all that deals with plain and simple functions. There's plenty of C++ features that deal with functions in an object-oriented way. First a small recap of old C-style function pointers.

Function pointers

Instead of a long story, just this example:

// Calculate and return the integral of the function f in the interval [a,b]
double integral(double (*f)(double), double a, double b) {
    // You can use f(x) as you would use any other function that
    // takes & returns a double
}

// Some sine function
double sin(double);

// Later...
double a = integral(&sin, 0.0, 1.0);

It's now possible to pass whatever function to integral, as long as it complies with the signature of integral, where double (*f)(double) defines a pointer f to a function that takes one double as an argument and returns a double. (You should use using to reduce clutter, the old variant is typedef. We'll learn later why you should prefer using.)

In traditional C, function pointers have lots of uses as "callback" functions, operations to be "mapped" on array elements, internals of text parsers, etc.

Functors

C++ gives you the ability to overload the function call operator operator(). This provides a mechanism to create classes whose objects can behave as if they were functions. The difference between functors and plain functions is that functors retain state through their fields. In other words functors can "remember" information that's local to them because they are objects and have their own data. Look at this conceptual example of a class representing a complex model that can be evaluated in a point x:

class Model {
    public:
        // Train model to fit training data
        void train(Data tdata);

        // Evaluate model in a point x
        double operator()(double x);

    private:
        // Whatever data is necessary to train and remember
};

double evaluate_model(Model m) {
    // Do fancy model evaluation
    // You can use m as if it was a function: double m(double)
}

// Now you can have many models trained on different inputs...
Model m1;
Model m2;
m1.train(data1);
m2.train(data2);

evaluate_model(m1);
evaluate_model(m2);

Polymorphic function wrapper

The next step is to wrap "anything that's callable" in a function wrapper. This is provided by the std::function<> wrapper from <functional>. The template parameter specifies the signature of the callable entity that's being wrapped.

Let's rewrite the integrate function from above:

#include <functional>

// Calculate and return the integral of the function f in the interval [a,b]
double integrate(std::function<double (double)> f, double a, double b) {
    // You can use f(x) as you would use any other function that
    // takes & returns a double
}

You can now do things like:

double d = integrate(sin, 0.0, 1.0); // C-style function pointer is wrapped!

Model m;
m.train(data);
double e = integrate(m, data.lowest(), data.highest());

All with the same integrate function!

Lambda functions

Lambda functions are very convenient in combination with the std::function<> wrapper. Consider an example of some iterative process that looks for a solution and stops when some conditions are met. For example: some tolerances have been met, or the process has been iterating for so long that no improvements are expected. This is a highly conceptual, yet very useful example, especially the part where the two conditions are combined easily to create a new one.

// Some iterative algorithm that repeats while
// requested conditions are not met
State find_solution(std::function<bool (State&)> terminate) {
    State s; // Initial state of the process

    while (!terminate(s)) {
        // Improve current state s
    }

    // Solution found
    return s;
}

// Some condition that will be true if State s meets
// a minimum tolerance of 1.0e-6
std::function<bool (State&)> min_tol_met = \
    [](State& s){
        return (s.tol < 1.0e-6) ? true : false;
    };

// Another condition that will be true if the number
// of iterations of a State s exceeds 1000
std::function<bool (State&)> num_it_reached = \
    [](State& s){
        return (s.nit > 1000) ? true : false;
    };

// This condition "wraps" the two above conditions
// in an OR fashion
std::function<bool (State&)> both_conditions = \
    [](State& s){
        return min_tol_met(s) || num_it_reached(s);
    };

// Let's do this!
State sol = find_solution(both_conditions);

Remember that we couldn't store a vector of lambda functions because they each have a unique type? Well, we can store a vector of polymorphic function wrappers:

#include <vector>
#include <iostream>
#include <functional>

int main() {
    const double pi = 3.14159265;
    std::string msg = "Hello";

    auto f = [&]() {
        msg += " after lambda!";
    };

    auto g = [=]() {
        std::cout << "I want pi! " << pi << std::endl;
    };

    std::vector<std::function<void ()>> fs = {f, g};

    for (auto f : fs) {
        std::cout << msg << std::endl;
        f();
        std::cout << msg << std::endl;
    }

    return 0;
}

Pointers to member functions

Different from regular function pointers?

Earlier in this session, you studied function pointers and other related subjects like functors and polymorphic function wrappers. You might have noticed that although a function and a functor could behave very similarly, the functor needs an object to be called on. Therefore, even if the arguments and return value are identical, you can't assign one to the other. For example:

#include <iostream>

using namespace std;

class Ftor {
    public:
        int operator()(int a, int b) {
            return a + b;
        }
};

int func(int a, int b) {
    return a + b;
}

// Shorthand notation for a function with the above signature, C++11 alias declaration!
using binary_funct_t = int (*)(int, int);

int main(int argc, char* argv[]) {
    int a = 1;
    int b = 2;
    Ftor ftor; // You need an object to call Ftor::operator()

    // Function and functor behave the same
    cout << func(a, b) << endl;
    cout << ftor(a, b) << endl;

    // But they don't have the same type
    binary_func_t gunc = func; // OK
    binary_func_t gtor = ftor.operator(); // ERROR!

    // All the rest is wishful thinking...
    cout << gunc(a, b) << endl;
    cout << gtor(a, b) << endl;

    return 0;
}

Play around with the above code. You'll get errors such as:

error: cannot convert 'Ftor::operator()' from type 'int (Ftor::)(int, int)'
    to type 'binary_func_t {aka int (*)(int, int)}'

Alias declarations: noticed how I used a C++11 alias declaration for binary_funct_t? If you want to program old-school (but you shouldn't), you could have used C++98 typedef. In the next session you will learn why alias declarations should be preferred over typedef.

Syntax

The function func is of the type int (*)(int, int) whereas Ftor::operator() has the type int (Ftor::*)(int, int); it's a pointer to a member function of Ftor and needs an object of type Ftor to be called on; this won't work:

// Define g to be a pointer to a member function of Ftor with two int
// arguments. Assign Ftor's operator() to g
int (Ftor::* g)(int, int);
g = &Ftor::operator();

// Try to call g
cout << g(a, b) << endl;

You'll have to do this instead:

int (Ftor::* g)(int, int);
g = &Ftor::operator();

Ftor gtor; // <- You NEED an existing object to call g on
// ... and this is the one and only right syntax to do so
cout << (gtor.*g)(a, b) << endl;

Or, if gtor is a pointer to an object of type Ftor:

Ftor* gtor = new Ftor;
cout << (gtor->*g)(a, b) << endl;

Another example

Of course the above is not limited to functors; all (non-static) member functions behave this way. Here's another example:

class SimpleMath {
    public:
        double sin(double);
        double cos(double);
        ...
};

// Define a pointer to member of SimpleMath
// which takes and returns a double
double (SimpleMath::* pSimpleMathFunc)(double);
pSimpleMathFunc = &SimpleMath::cos;

// You NEED an object to call the actual function
SimpleMath sm;
double x = (sm.*pSimpleMathFunc)(2.0);

// or a pointer to an object
SimpleMath* pSm = ...
double y = (pSm->*pSimpleMathFunc)(1.0);

Inheritance

To make matters even more complex, pointers to member functions can be used in the context of derived classes as well. Here's an example:

#include <iostream>

using namespace std;

class Base {
    public:
        virtual void print_info() const {
            cout << "I'm a Base object" << endl;
        }
};

class Derived : public Base {
    public:
        virtual void print_info() const {
            cout << "I'm a Derived object" << endl;
        }
};

int main() {

    using BaseMemFun_t = void (Base::*)() const;

    BaseMemFun_t pBMF = &Base::print_info;

    // Calling pBMF on a Base object
    Base b;
    (b.*pBMF)();

    // Calling pBMF on a Derived object
    Derived d;
    (d.*pBMF)();

    // Calling pBMF on a pointer to a Base object
    Base * pb = new Derived;
    (pb->*pBMF)();

    return 0;
}

As you would expect, you can't go the other way around and try to call a pointer to a member of Derived on an instance of Base. This would require an implicit downcast in the class hierarchy. But one can safely assign a pointer to a member of a base class to a pointer to a member of a derived class.

using BaseMemFun_t = void (Base::*)() const;
using DerivedMemFun_t = void (Derived::*)() const;

BaseMemFun_t pBMF = &Base::print_info;
DerivedMemFun_t pDMF = pBMF; // OK

// Calling a Base member func. on a pointer to a Derived object
Derived * pd = new Derived;
(pd->*pDMF)();

// Calling a Derived member func. on a pointer to a Base object
Base * pb = new Derived;
(pb->*pDMF)();

// Calling a Derived member func. on a pointer to a Base object
Base * pb2 = new Base;
(pb2->*pDMF)();

Gives:

error: pointer to member type 'void (Derived::)()const' incompatible with
       object type 'Base'
     (pb->*pDMF)();
           ^
error: pointer to member type 'void (Derived::)()const' incompatible with
       object type 'Base'
     (pb2->*pDMF)();
            ^

Function wrapper

Remember the polymorphic function wrapper from #include <functional>? It could also be used to wrap both a function and a functor properly:

Ftor ftor;
std::function<int (int, int)> gunc = func;
std::function<int (int, int)> gtor = ftor;

std::cout << gunc(a, b) << std::endl;
std::cout << gtor(a, b) << std::endl;

This works because both func and ftor are both directly callable, but std::function can also be used to wrap other member functions. For example:

std::function<void(Base*)> f = &Base::print_info;
Base b;
f(&b);

In this case you still need an instance of type Base to actually call the member function, but it is also possible to bind that instance directly when the std::function is assigned:

Base b;
std::function<void(void)> f = std::bind(&Base::print_info, b);
f();

Wrapping the call in a lambda function would have a similar effect:

 std::function<void(void)> f = [&b](){b.print_info();};

Find out in the next section how this works!

Remarks

  • Pointers to member functions are a pretty complicated subject that can generate very hairy compiler error messages. They are not used that frequently since there are often better and more clear ways to express similar functionality using, for instance, std::function objects.
  • Remember: a member function is meaningless without an object to invoke on! Member function pointers cannot be dereferenced (have their function called) directly by themselves; they need an object to operate on.
  • Use using (alias declarations) for more clarity: using SMFunc = double (SimpleMath::*)(double). This allows you to do: SMFunc f = &SimpleMath::sin.
  • Read: C++FAQ: Pointers to member functions
  • Read: C++FQA: Pointers to member functions

Threading

C++11 introduces basic facilities for threading in the standard library. "Basic" in this case means that threads are handled in a pretty low-level manner with no scheduling or any implicit thread safety. Note that on some systems, to run this thread functionality you'll have to add the following flag in your CMakeLists.txt:

SET(CMAKE_CXX_FLAGS -pthread)

Have a look at the following examples.

Example 0

#include <iostream>
#include <thread>

// The function that will be called in a separate thread
void thread_func() {
    std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl;
}

int main() {
    // Launch one thread that will execute the code of thread_func
    std::thread t1(thread_func);
    std::thread t2(thread_func);

    // Wait for the thread to finish
    t1.join();
    t2.join();

    return 0;
}

Example 1

#include <iostream>
#include <thread>

int main() {
    // Launch one thread that will execute the code of the lambda
    std::thread t([](){
        cout << "Hello from thread " << std::this_thread::get_id() << endl;
    });

    // ... Do other stuff while thread t is busy

    // Wait for the thread to finish
    t.join();

    return 0;
}

Example 2

What goes wrong here? Make sure to request many threads. Depending on how fast your computer is, you might need to create some additional load on your processor for this example to work.

#include <iostream>
#include <vector>
#include <thread>
#include <iostream>

void thrFunc(int& a) {
    ++a;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cout << argv[0] << " num_threads" << std::endl;
        return 1;
    }
    int num_threads = atoi(argv[1]);

    // Accumulator
    int a = 0;

    // Create & launch some threads
    std::vector<std::thread*> threads;
    for (size_t idx = 0; idx < num_threads; ++idx) {
        threads.push_back(new std::thread(thrFunc, std::ref(a)));
    }

    // "Barrier"
    for (std::thread * t : threads) {
        t->join();
    }

    if (a == num_threads) {
        std::cout << "OK!" << std::endl;
    } else {
        std::cout << "What?? a is " << a << ", should be " << num_threads << std::endl;
    }

    // Cleanup
    for (std::thread * t : threads) {
        delete t;
    }

    return 0;
}

Example 3

We can fix this by introducing an std::mutex around the sensitive operation:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

void thrFunc(int& a, std::mutex& a_mtx) {
    a_mtx.lock();
    ++a;
    a_mtx.unlock();
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cout << argv[0] << " num_threads" << std::endl;
        return 1;
    }
    int num_threads = atoi(argv[1]);

    // Accumulator & its mutex
    int a = 0;
    std::mutex a_mtx;

    // Create & launch some threads
    std::vector<std::thread*> threads;
    for (size_t idx = 0; idx < num_threads; ++idx) {
        threads.push_back(new std::thread(thrFunc, std::ref(a), std::ref(a_mtx)));
    }

    // "Barrier"
    for (std::thread * t : threads) {
        t->join();
    }

    if (a == num_threads) {
        std::cout << "OK!" << std::endl;
    } else {
        std::cout << "What?? a is " << a << ", should be " << num_threads << std::endl;
    }

    // Cleanup
    for (std::thread * t : threads) {
        delete t;
    }

    return 0;
}

C++11 also introduced an atomic operations library with which you can solve this problem. Can you rewrite the code, so you would not need mutex?

Reading material

Please read the following material:

Exercises

Delivery Observer

Go to the assignment: https://classroom.github.com/a/ISxOWwJf

String copy

Go to the assignment: https://classroom.github.com/a/wenh-XAm

Parallel daxpy

Go to the assignment: https://classroom.github.com/a/dMy23ky5

Plotting polynomials, part 3

Go to the assignment: https://classroom.github.com/a/oUnxfMg-