I’m always disappointed when my Google search doesn’t turn up useful results on the first page. Often, an answer to a C++ question takes me to a StackOverflow page, or to a cplusplus page, where I usually find what I want. This time I didn’t find what I want on the first page (the stackoverflow link wasn’t quite it), hence this blog post.
I’ve just started a series on test-focused learning (TFL) for the new C++11 features. I’m jumping the gun a little here since I’ve not yet covered lambdas (or auto, or the range-based for, or rvalue references), but I wanted searchers to have (what I think is) a better answer on the first page.
The story: Iterate a vector of objects, adding into a sum by dereferencing a member on each object, then return the sum from a function.
Here’s the declaration for the Item class:
class Item {
public:
Item(int cost) : cost_{cost} {}
int Cost() { return cost_; }
private:
int cost_;
};
Here’s the assertion (I coded this test-first, of course):
ASSERT_THAT(
TotalCost({Item(5), Item(10), Item(15)}),
Eq(5 + 10 + 15));
You can get this test to pass in three statements using the range-based for loop.
int TotalCost(vector&& items) {
int total{0};
for (auto item: items) total += item.Cost();
return total;
}
That’s clean and simple to understand, explanation of syntax barely needed.
Here’s the implementation using accumulate and a lambda.
int TotalCost(vector&& items) {
return accumulate(items.begin(), items.end(), 0,
[] (int total, Item item) { return total + item.Cost(); });
}
That’s it. If you’re not familiar with accumulate (I wasn’t, hence my Google search), it takes a range, an initial value, and a function. If you’re not familiar with using lambdas to declare functions:
-
[] declares that the lambda requires no capture of other variables
-
between () is the parameter list for the arguments that accumulate passes to the function
-
between the {} is the function’s implementation
Which do you prefer (or neither), and why?
Comments
Franklin Chen January 18, 2013 at 2:07 pm
It should be + rather than += in
return accumulate(items.begin(), items.end(), 0,
[] (int total, Item item) { return total + item.Cost(); });
also the += happens to be legal and have a side effect that does not matter in this case.
In a toy sequential program, I don’t really care how the sum is implemented; it’s O(n) anyway. In a real program summing a huge data set, of course we would want to get parallel speedup by means of the standard map/reduce abstraction. We might not want to have a vector at all, but a parallel vector instead, for example.
Jeff Langr January 18, 2013 at 2:14 pm
Thanks Franklin,
Good catch on the +=, that was inadvertent (copied quickly from the code for the range-based for loop). Fixed.
The article is focused on readability/understanding of the C++11 features. Either construct is still applicable in non “toy” programs–not all “real” programs require that level of optimization.
-J-
Franklin Chen January 18, 2013 at 7:57 pm
I remember downloading STL using ftp from HP in 1994 over a modem Internet connection and being all excited about it, as a software engineer using C and C++ at the time, but also at the same time I was getting into functional programming. I do have to confess to having a warm fuzzy feeling about now being able to use the lambda in C++11 if were still actively programming in C++ these days. Not having to manually lambda-lift and create structs to simulate closures is going to be a big win for working C++ programmers, I predict.
christi parks January 25, 2013 at 5:53 am
I am not a programmer but I have this C language subject this session and have to prepare for it. What all topics should be covered in it?
And has anyone studied from this course http://www.wiziq.com/course/2118-learn-how-to-program-in-c-language of C tutorial online?? or tell me any other guidance…
would really appreciate help
Bret Kuhns March 25, 2013 at 8:19 am
I definitely prefer the more functional style version over the imperative loop. Looking a bit into the future, it’ll only get better with standard range-based algorithms in C++17 and the new lambda changes proposed for C++14…
return accumulate(items, 0, [] (int total, const Item& item) total + item.Cost());