C++11 has brought a lot of new features. In this post I will some of my favorite changes and compare it to C++98. The source used here can also be downloaded from my GitHub account.
Lambda expressions
This is a very useful addition! If you have used STL for a while and feel that it is very cumbersome to pass functors to STL algorithms, then this is for you. With lambda object, you can easily create unnamed functions that can be passed directly instead of having to use different binders.
These two examples does the same thing. The first one is written with C++98 using a functor and bind2nd:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Add i to all objects in the container myInts struct addValue: binary_function<int, int, void> { void operator() (int &a, const int b) const { a += b;} }; std::for_each(myInts.begin(), myInts.end(), std::bind2nd<addValue>(addValue(), i)); |
And now the same thing using C++11 lambda expression:
1 2 3 4 5 6 7 8 |
// Add i to all objects in the container myInts. for_each(std::begin(myInts), std::end(myInts), [i](int &value) { value += i; }); |
Once you understand the new syntax for lambda expressions you will find that the code is a lot easier to read. At least for smaller expressions. If the expression is more complex then it can be better make a named function of it instead.
Ranged-based for loops
I didn’t see the new for loops as a very big change in the beginning. But that was before I wrote the first for loop to iterate over a container…
If you wanted to iterate over a container in C++98, you did something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Return the sum of all ints in the container myInts. int myC98class::sum() { int total = 0; for (std::vector<int>::iterator it = myInts.begin(); it != myInts.end(); ++it) { total += *it; } return total; } |
Now, this is a stupid example. I hope no-one would actually write this… Oh, I just did. Better use std::accumulate next time. But the point is to show for loops so let’s ignore that there are better ways.
In C++1 1 you can write the same thing like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Return the sum of all ints in the container myInts. int myC11class::sum() { int total = 0; for (int i: myInts) { total += i; } return total; } |
This is a much cleaner way to loop over the contents of an iterator. It is worth noting that the variable i in the loop is a copy of the current element in the container. For an int it doesn’t matter. But if the container holds larger objects or if you want to update the contents in the container, then you need to use a reference instead.
Threads
If you wanted to use threads in previous versions of C++, chances are that you used pthreads. This library was originally written for C and does not really a good fit for C++. But in C++11 we can suddenly create threads in several ways!
Let us first look at the old way of doing thing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void *partialSum(void *ptr) { std::vector<int>::iterator *itPair = (std::vector<int>::iterator *)ptr; int *total = new int(0); *total = std::accumulate(itPair[0], itPair[1], 0); pthread_exit((void*)total); } int myC98class::sumWithThread() { pthread_t thread1, thread2; std::vector<int>::iterator itT1[2]; std::vector<int>::iterator itT2[2]; itT1[0] = myInts.begin(); itT1[1] = itT1[0]+5; itT2[0] = itT1[1]; itT2[1] = myInts.end(); pthread_create(&thread1, NULL, &partialSum, (void*)&itT1); pthread_create(&thread2, NULL, &partialSum, (void*)&itT2); int *ret1, *ret2; pthread_join(thread1, (void **)&ret1); pthread_join(thread2, (void **)&ret2); int sum = *ret1 + *ret2; delete ret1; delete ret2; return sum; } |
This wasn’t very pretty. You have to mess around with a lot of pointers to get the job done.
In C++11 we can do the same thing like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
int partialSum(std::vector<int>::iterator it1, std::vector<int>::iterator it2) { return std::accumulate(it1, it2, 0); } int myC11class::sumWithThread() { std::packaged_task<int(std::vector<int>::iterator, std::vector<int>::iterator)> task1(partialSum); std::packaged_task<int(std::vector<int>::iterator, std::vector<int>::iterator)> task2(partialSum); std::future ret1 = task1.get_future(); std::future ret2 = task2.get_future(); std::thread th1(move(task1), std::begin(myInts), std::begin(myInts)+5); std::thread th2(move(task2), std::begin(myInts)+5, std::end(myInts)); int sum = ret1.get(); sum += ret2.get(); th1.join(); th2.join(); return sum; } |
We don’t need to care about pointers or deleting memory allocated for the returned value. But it is still a bit complex. Lets try again:
1 2 3 4 5 6 7 8 9 10 11 12 |
int partialSum(std::vector<int>::iterator it1, std::vector<int>::iterator it2) { return std::accumulate(it1, it2, 0); } int myC11class::sumWithAsync() { auto ret1 = std::async(std::launch::async, partialSum, std::begin(myInts), std::begin(myInts)+5); auto ret2 = std::async(std::launch::async, partialSum, std::begin(myInts)+5, std::end(myInts)); return ret1.get() + ret2.get(); } |
Now, this is simple! You could also eliminate the need of the named function partialSum() by using a lambda expression instead.
Want to know more? There are many good sites to visit. Herb Sutter has a lot of information. So does Bjarne Stroustrup. You can also read Bjarnes book The C++ Programming Language, fourth edition.
Leave a Reply