CS253: Software Development with C++

Fall 2020

Initializer Lists

Show Lecture.InitializerLists as a slide show.

CS253 Initializer Lists

Initialization

Consider this code:

vector<int> v = {11,22,33,44,55};
set<int> s    = {11,22,33,44,55};

initializer_list

The name

Why is the name so god-awful long?

Standalone

Since an initializer_list has iterators, .begin(), and .end(), we can use it in a for loop:

for (auto v : {11,22,33,44,55})
    cout << v << ' ';
11 22 33 44 55 

or, more explicitly:

auto il = {11,22,33,44,55};
auto it = il.begin() + 3;
cout << *it << '\n';
44

or, even:

auto il = {11,22,33,44,55};
cout << *(il.begin()+3) << '\n';
44

Example

This can be used to avoid an array or vector:

string days[] = {"Mo","Tu","We","Th","Fr","Sa","Su"};
for (auto dow : days)
    cout << dow << "·";
Mo·Tu·We·Th·Fr·Sa·Su·
for (auto dow : {"Mo","Tu","We","Th","Fr","Sa","Su"})
    cout << dow << "·";
Mo·Tu·We·Th·Fr·Sa·Su·

Interaction with standard containers

So, how does this work?

unordered_set<int> us = {345,678,901,234,567,890};
for (auto v : us)
    cout << v << ' ';
890 567 234 901 678 345 

Constructor using initializer_list

unordered_set(const initializer_list<value_type> &);
unordered_set<value_type> &operator=(const initializer_list<value_type> &)

Not Free

Example

class Hundred {
    int data[100];
  public:
    int &operator[](int n) { return data[n]; }
};

Hundred h;
h[1] = 123;
h[4] = 456;
cout << h[1]+h[4] << '\n';
579

Example that fails

class Hundred {
    int data[100];
  public:
    int &operator[](int n) { return data[n]; }
};

Hundred h = {11,22,33,44,55};
cout << h[1]+h[4] << '\n';
c.cc:7: error: could not convert '{11, 22, 33, 44, 55}' from '<brace-enclosed 
   initializer list>' to 'main()::Hundred'

Example

class Hundred {
    int data[100];
  public:
    Hundred(const initializer_list<int> &il) {
        copy(il.begin(), il.end(), data);
    }
    int &operator[](int n) { return data[n]; }
};

Hundred h = {11,22,33,44,55};
cout << h[1]+h[4] << '\n';
77

Example

class Hundred {
    int data[100];
  public:
    Hundred(const initializer_list<int> &il) {
        copy(il.begin(), il.end(), data);
    }
    int &operator[](int n) { return data[n]; }
};

Hundred h = {11,22,33,44,55};
cout << h[1]+h[4] << '\n';
h = {6,7,8,9};
cout << h[2] << '\n';
77
8

How did that work?

Example

class Hundred {
    int data[100];
  public:
    Hundred(const initializer_list<int> &il) {
        *this = il;
    }
    Hundred &operator=(const initializer_list<int> &il) {
        copy(il.begin(), il.end(), data);
        return *this;
    }
    int &operator[](int n) { return data[n]; }
};

Hundred h = {11,22,33,44,55};
cout << h[1]+h[4] << '\n';
h = {6,7,8,9};
cout << h[2] << '\n';
77
8

That’s better.

Don’t be fooled

Of course, sometimes, a brace is just a brace:

int a[5] = {11, 22, 33}; // initializer_list<int>?
cout << a[0] + a[2] << '\n';
44

Or, a more brutal example:

cout << {11,22,33}.size() << '\n';
c.cc:1: error: expected primary-expression before '{' token

Somehow, the compiler figures it out.

Introspection on the cheap

Here’s a cheap way to learn the type of anything:

auto a = {1,2};
throw a;
terminate called after throwing an instance of 'std::initializer_list<int>'
SIGABRT: Aborted

-or-

auto b = {3,4};
return b;
c.cc:2: error: cannot convert 'std::initializer_list<int>' to 'int' in return

More introspection

throw 1e6;
terminate called after throwing an instance of 'double'
SIGABRT: Aborted
throw 1234567890;
terminate called after throwing an instance of 'int'
SIGABRT: Aborted
throw 123456789012345;
terminate called after throwing an instance of 'long'
SIGABRT: Aborted
throw 0xfedcba9876543210;
terminate called after throwing an instance of 'unsigned long'
SIGABRT: Aborted

Introspection with strings

throw 'a';
terminate called after throwing an instance of 'char'
SIGABRT: Aborted
throw "b";
terminate called after throwing an instance of 'char const*'
SIGABRT: Aborted
throw "c"s;
terminate called after throwing an instance of 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >'
SIGABRT: Aborted