13 Apr 2013 maragato   » (Master)

Container changes in C++11

The recently approved C++11 standard brings a lot of welcome changes to C++ that modernize the language a little bit. Among the many changes, we find that containers have received some special love.

Initialization

C++ was long behind modern languages when it came to initializing containers. While you could do

int a[] = {1, 2, 3};

for simple arrays, things tended to get more verbose for more complex containers:

vector<string> v;
v.push_back("One");
v.push_back("Two");
v.push_back("Three");

C++11 has introduced an easier, simpler way to initialize this:

vector<string> v = {"One", "Two", "Three"};

The effects of the changes are even better for things like maps, which could get cumbersome quickly:

map<string, vector<string> > m;
vector<string> v1;
v1.push_back("A");
v1.push_back("B");
v1.push_back("C");

vector<string> v2;
v2.push_back("A");
v2.push_back("B");
v2.push_back("C");

m["One"] = v1;
m["Two"] = v2;

This can now be expressed as:

map<string, vector<string>> m = {{"One", {"A", "B", "C"}},
                                 {"Two", {"Z", "Y", "X"}}};

Much simpler and in line with most modern languages. As an aside, there’s another change in C++11 that would be easy to miss in the code above. The declaration

map<string, vector<string>> m;

was illegal until now due to >> always being evaluated to the right-shift operator; a space would always be required, like

map<string, vector<string> > m

No longer the case.

Iterating

Iterating through containers was also inconvenient. Iterating the simple vector v above:

for (vector<string>::iterator i = v.begin();
     i != v.end(); i++)
    cout << i << endl;

Modern languages have long had some foreach equivalent that allowed us easier ways to iterate through these structures without having to explicitly worry about iterators types. C++11 is finally catching up:

for (string s : v)
    cout << s << endl;

As well, C++11 brings in a new keyword, auto, that will evaluate to a type in compile-type. So instead of

for (map<string, vector<string> >::iterator i = m.begin();
     i != m.end(); i++) {

we can now write

for (auto i = m.begin(); i != m.end(); i++) {

and auto will evaluate to map<string, vector<string>>::iterator.

Combining these changes, we move from the horrendous

for (map<string, vector<string> >::iterator i = m.begin();
     i != m.end(); i++)
    for (vector<string>::iterator j = i->second.begin();
         j != i->second.end(); j++)
        cout << i->first << ': ' << *j << endl;

to the much simpler

for (auto i : m)
    for (auto j : i.second)
        cout << i.first << ': ' << j << endl;

Not bad.

C++11 support varies a lot from compiler to compiler, but all of the changes above are already supported in the latest versions of GCC, LLVM, and MSVC compilers.

Syndicated 2012-12-15 14:41:36 from robteix dot com

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!