Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does this C++ / C++11 construction mean?

Tags:

c++

c++11

I've got this short snippet of code. I don't understand what this construction means. I know this snippet of code reads numbers from input and counts its frequency in an unordered_map. But what is [&]? And what is the meaning of (int x)? What does the input(cin) stand for? I mean the "cin" in parentheses? And how can for_each iterate from input(cin) to empty eof parameter? I don't understand of this whole construction.

unordered_map<int,int> frequency;
istream_iterator<int> input(cin);
istream_iterator<int> eof;

for_each(input, eof, [&] (int x)
    { frequency[x]++; });
like image 396
Robert Balent Avatar asked Sep 26 '12 08:09

Robert Balent


2 Answers

istream_iterator allows you to iteratively extract items from an istream, which you pass in to the constructor. The eof object is explained thus:

A special value for this iterator exists: the end-of-stream; When an iterator is set to this value has either reached the end of the stream (operator void* applied to the stream returns false) or has been constructed using its default constructor (without associating it with any basic_istream object).

for_each is a loop construct that takes iterator #1 and increments it until it becomes equal with iterator #2. Here it takes the iterator that wraps standard input cin and increments it (which translates to extracting items) until there is no more input to consume -- this makes input compare equal to eof and the loop ends.

The construct [&] (int x) { frequency[x]++; } is an anonymous function; it is simply a shorthand way to write functions inline. Approximately the same effect could be achieved with

unordered_map<int,int> frequency; // this NEEDS to be global now
istream_iterator<int> input(cin);
istream_iterator<int> eof;

void consume(int x) {
    frequency[x]++;
}

for_each(input, eof, consume);

So in a nutshell: this code reads integers from standard input until all available data is consumed, keeping a count of each integer's appearance frequency in a map.

like image 135
Jon Avatar answered Oct 03 '22 23:10

Jon


There are two parts of your question.

  1. The first one concerns stream iterators. An std::istream_iterator<T> is constructed from some std::istream & s, and upon dereferencing, it behaves like { T x; s >> x; return x; }. Once the extraction fails, the iterator becomes equal to a default-constructed iterator, which serves as the "end" iterator.

    Stream iterators allow you to treat a stream as a container of tokens. For example:

    std::vector<int> v(std::istream_iterator<int>(std::cin),
                       std::istream_iterator<int>());
    
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    
  2. C++11 introduces lambda expressions, which define anonymous functions or functors (called closures). A simple one looks like this:

    auto f = [](int a, int b) -> double { return double(a) / double(b); };
    
    auto q = f(1, 2);  // q == 0.5
    

    This f could have been written as an ordinary, free function, but the free function would have had to appear at namespace scope or as a static member function of a local class. (Which is in fact what the type of the lambda expression is!) Note that the type of a lambda expression is unknowable, and can only be captured via the new auto keyword.

    Lambdas become more useful when they act as complex function objects which can capture ambient state. Your example could have been written like this:

    auto f = [&frequency](int x) -> void { ++frequency[x]; };
    

    The variables that appear in between the first square brackets are captured. This lambda is equivalent to the following local class and object:

    struct F
    {
        F(std::unordered_map<int, int> & m) : m_(m) { }
        void operator()(int x) { ++m_[x]; }
    private:
        std::unordered_map<int, int> & m_;
    } f;
    

Variables without & in the capture list are captured by value, i.e. a copy is made in the closure object. As a short-hand, you can say [=] or [&] to capture everything respectively by value or by reference.

like image 39
Kerrek SB Avatar answered Oct 03 '22 23:10

Kerrek SB