The University of Iowa
The College of Liberal Arts and Sciences
Department of Computer Science

Programming Languages and Tools:

CS:3210:0001

Lecture/Lab #22

Programming with C++

Strict weak ordering, iterators

Warm up

struct organism {
    organism( point const& pos )
        : pos_( pos ) {}

    point position() const { return pos_; }

private:
    point pos_;
};

Given the following definition of class `organism`:

... what data structure would you use to represent a list of the organisms populating a rectangular map, assuming that only one organism can occupy a specific position?

std::map/set Ordering

  • By default std::map/set elements are ordered using std::less predicate, which forwards to operator <.

  • We can provide our own comparison criteria:

set<student, compare_email> class_roster;
  • The specified comparison function must induce a strict weak ordering on the container's keys.

(1, 1)

(2, 3)

X

Y

A binary relation that we'll name < that has the following properties:

  • Irreflexive: it's never the case that a < a;

  • Asymmetric: if a < b, then it's never the case that b < a;

  • Transitive: if a < b and b < c, then it's always the case that a < c;

  • Transitively incomparable: if a is incomparable with b (neither a < b nor b < a hold), and b is incomparable with c, then it's always the case that a is incomparable with c.

Strict weak ordering

bool operator<( point const& l, point const& r )
{
    return l.x < r.x && l.y < r.y;
}

Does our `operator<` satisfy these requirements?

Strict weak ordering (cont.)

What happens if our `operator <` fails to satisfy these requirements?

  • Nothing — as long as users of our type don't try to use it as a key in one of the standard ordered associative containers

  • Otherwise undefined behavior — with consequences ranging from unexpected results to run-time crashes.

bool operator<( point const& l, point const& r )
{
    return std::pair( l.x, l.y ) < std::pair( r.x, r.y );
}

How to write `operator<` that imposes strict weak ordering on point objects

For any type that can conceivably be placed in std::set or used as a key in std::map, operator< has two jobs:

  • Define a relation that makes intuitive sense for users of the type (if ( a < b ) ...);

Strict weak ordering implications

  • Define a relation that imposes a strict weak ordering on objects of the type, so that it can be safely used in ordered associative containers.

These two distinct jobs of the operator< — providing a natural "less than" relation that makes sense in the class' domain vs. defining an ordering of the class' objects for the standard library — can sometimes be at odds with each other.

Guideline

If your class provides an operator< that does not satisfy strict weak ordering requirements, make sure to also specialize std::less with an implementation that does.

Iterators

Objects with a well-defined interface that enable us to iterate/traverse over elements of a container without knowing the container's underlaying data structure.

In C++, the standard iterator interface is that of a generalized pointer.

std::vector<student> roster1 = { ... };
std::set<student> roster2 = { ... };

for ( auto const& s: roster1 )
    std::cout << s << "\n";

for ( auto const& s: roster2 )
    std::cout << s << "\n";

Iterators (cont.)

for ( auto const& s: roster1 )
    std::cout << s << std::endl;
{
    auto iter = roster1.begin();
    auto end = roster1.end();
    for ( ; iter != end; ++iter )
        std::cout << *iter << std::endl;
}    

[ begin, end )

half-open interval aka inclusive/exclusive range

v.end()

v.begin()

...

Iterator Operations

Obtaining an iterator to the beginning of a sequence:

Moving forward in a sequence:

Recognizing an end of a sequence:

begin( seq )
++i
i != end( seq )

Accessing the element:

*i

Moving backward in a sequence:

--i

Advance to an nth element (O(1) arithmetics):

i += n, i + n, i -= n, i - n, i - j

Compare iterator position:

i < j, i <= j, i > j, i >= j

any

any

any

any

bidirectional

random access

random access

Iterator category

C++ vs Python's iterators

people = set([ "jack", "sape" ]) 
for k in people:
    print(k)

print("-------------------")

iterator = people.__iter__()
while True:
    try:
        item = iterator.__next__()
        print(item)
    except StopIteration:
        break
set<string> people{ "jack", "sape" };
for ( auto k : people )
    std::cout << k << "\n";

std::cout << "-----------------\n";

auto iter = people.begin();
auto end = people.end();
for ( ; iter != end; ++iter )
    std::cout << *iter << "\n";

Python

C++

Exercise 1

Write a function `find` that takes a vector of strings and a string value to search for and returns an iterator to the matching vector element

  • Open the exercise template

  • Write your code, press Run to test

  • When you're done, grab your Repl's link and send it as a direct message to me (agurtovoy)

  • Click on the corresponding option in the "Lab22 exercises" poll in #general

Programming with C++, Spring 2020, Lecture #22

By Aleksey Gurtovoy

Programming with C++, Spring 2020, Lecture #22

Strict weak ordering, iterators

  • 598