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.

Also read...

Comments

Leave a Reply