CS253: Software Development with C++

Spring 2021

Pair And Tuple

Show Lecture.PairAndTuple as a slide show.

CS253 Pair And Tuple

pair

pair<string, int> who("Jack", 253);
cout << who.first << " teaches " << who.second;
Jack teaches 253
pair<string, int> who("Jack", 253);
cout << who[0];  // nope
c.cc:2: error: no match for 'operator[]' in 'who[0]' (operand types are 
   'std::pair<std::__cxx11::basic_string<char>, int>' and 'int')

pair definition

template<typename F, typename S>
struct pair {
    F first;
    S second;
};

map and pair

pair comparison

vector<pair<string, string>> ff = {
    {"Storm", "Johnny"},
    {"Storm", "Sue"},
    {"Grimm", "Ben"},
    {"Richards", "Reed"},
};
sort(ff.begin(), ff.end());
for (auto &p : ff)
    cout << p.second << ' ' << p.first << '\n';
Ben Grimm
Reed Richards
Johnny Storm
Sue Storm

pair & multimap

multimap<string, string> ff = {
    {"Storm", "Johnny"},
    {"Storm", "Sue"},
    {"Grimm", "Ben"},
    {"Richards", "Reed"},
};
for (auto &p : ff)
    cout << p.second << ' ' << p.first << '\n';
Ben Grimm
Reed Richards
Johnny Storm
Sue Storm

tuple

tuple

using person = tuple<string, float, int, bool>;
person orange("DJT", 75, 239, false);
cout << boolalpha
     << "Name:    " << get<0>(orange)     << "\n"
     << "Height:  " << get<float>(orange) << "″\n"
     << "Weight:  " << get<2>(orange)     << "#\n"
     << "Popular: " << get<bool>(orange)  << "\n";
Name:    DJT
Height:  75″
Weight:  239#
Popular: false

Output

You can’t just << a pair or a tuple. What would go between the elements?

pair<int,int> p(3,4);
cout << p;
c.cc:2: error: no match for 'operator<<' in 'std::cout << p' (operand types are 
   'std::ostream' {aka 'std::basic_ostream<char>'} and 'std::pair<int, int>')

Instead, display both .first and .second, with something inbetween:

pair<int,int> p(1,2);
cout << p.first << '/' << p.second;
1/2

get with pair

pair<string,float> p("π", 3.14159);
cout << p.first        << '\n'
     << get<string>(p) << '\n'
     << get<0>(p)      << '\n'
     << p.second       << '\n'
     << get<float>(p)  << '\n'
     << get<1>(p)      << '\n';
π
π
π
3.14159
3.14159
3.14159

Structured binding

You can assign several variables at once (from a pair, tuple, C array, or just a struct). This is called structured binding. It was formerly accomplished with tie():

Extracting values from a pair:

pair p("pi", 3.14159);
auto [s, f] = p;
cout << s << ' ' << f << '\n';
pi 3.14159

References to values in a tuple:

tuple t(1.2, 44, 'x');
auto &[d, i, c] = t;
cout << d << ' ' << i << ' ' << c << '\n';
1.2 44 x

More structured binding

Const references to values in a C array:

int a[] = {11,22};
const auto & [x, y] = a;
cout << x << ' ' << y << '\n';
11 22

Copying values from a struct:

struct transaction {
    string currency;
    const char *symbol;
    double amount;
};
struct transaction trans{"Euro", "€", 1.23};
auto [cur, sym, amt] = trans;
cout << cur << '/' << sym << '/' << amt << '\n';
Euro/€/1.23

Uses for structured binding

map without structured binding

map<int, string> numbers = {
    { 3, "three" },
    { 0, "zero"  },
    { 2, "two"   },
    { 1, "one"   },
};

numbers[5] = "five";
numbers[4] = "four";

for (auto p : numbers)
    cout << p.first << " is " << p.second << '\n';
0 is zero
1 is one
2 is two
3 is three
4 is four
5 is five

map with structured binding

map<int, string> numbers = {
    { 3, "three" },
    { 0, "zero"  },
    { 2, "two"   },
    { 1, "one"   },
};

numbers[5] = "five";
numbers[4] = "four";

for (auto [left,right] : numbers)
    cout << left << " is " << right << '\n';
0 is zero
1 is one
2 is two
3 is three
4 is four
5 is five

A wee bit faster

map<int, string> numbers = {
    { 0, "zero" },
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
};

numbers[5] = "five";
numbers[4] = "four";

for (const auto & [left,right] : numbers)
    cout << left << " is " << right << '\n';
0 is zero
1 is one
2 is two
3 is three
4 is four
5 is five

CTAD

The C++17 feature Class Template Argument Deduction, or CTAD, used in the structured binding examples, allows the omission of the type in some cases when a container is being initialized.

pair<int, string> me(253, "Jack");  // explicit types
pair him(314, "Dave");              // implicit types, like auto
tuple who(165, "Sudipto");          // implicit types, like auto
cout << me.first      << " taught by " << me.second   << '\n'
     << him.first     << " taught by " << him.second  << '\n'
     << get<int>(who) << " taught by " << get<1>(who) << '\n';
253 taught by Jack
314 taught by Dave
165 taught by Sudipto

This makes variable initialization such as pair him a lot simpler.

What type is him.first?

It’s the same type as 314, which is int.

What type is him.second?

Same type as "Dave", which is const char *, not std::string.