Essential C++ # 03. Generic Programming

C++

 

Chapter 3. Generic Programming

In this chapter, the word "generic" refers to be independent of both the type of element they are operating on and the type of container within which the elements are held.

3.1. The Arithmetic of Pointers

📌Find specific value in a vector

 

📌(Generic Version)Find specific value in a vector

 

📌The magic and fun fact of pointer

Do you know in C++, an array is also a pointer? This is so much fun. The followings are the same!

If you are a C# programmer, you would say what the heck?? 😨😨

That's because an array is a pointer which records the 1st address of that array. Therefore, they are the same.

 

📌Find specific value in an array(Normal Version)

 

📌Find specific value in an array(Pointer Version)

//TODO , here 🤔, in Qt, the function declaration should remove const from const T *array. Is it because the new standard of C++?

 

📌Find specific value in an array(Pointer Version with sentinel)

In this version, we use the address of last element of the array as sentinel1 address.

You can use it as the following:

 

📌Behind the vec.begin()

Since a vector could be empty, therefore, it may cause error if we directly query like vec[0]. A safer way would be like this:

 

3.2. Iterators

The iterators here are very similar to the IEnumerable in C#. In short, iterator is a set of classes that are programmed using the same syntax as that of a pointer to collection in STL. For example, the ++ for vector is to query the next element, so as ++ for linked list. But the next address of a linked list cannot be just incremented. Therefore, we can override their operator.

 

📌What is an iterator?

iter is defined to be an iterator for vectors of T elements. It is initialized to address the first element of a vector.

The double colon :: indicates that iterator is a type nested within the T vector definition.

 

📌Example Function using iterator

//TODO currently this is compiled with error

The display function can be implemented like so:

T could be saw as the element type of that container.

 

//TODO why in the end return last but not 0?

The find function can be implemented like so:

T could be saw as the iterator type, while S could be saw as the element type.

 

You can use 1

 

📌Frequently Used Method

  • search algorithms: find(), count(), adjacent_find(), find_if(), count_if(), binary_search(), and find_first_of()
  • sorting and general ordering algorithms: merge(), partial_sort(), partition(), random_shuffle(), reverse(), rotate(), and sort()
  • copy, deletion, and substitution and algorithms: copy(), remove(), remove_if(), replace(), replace_if(), swap(), and unique()
  • relational algorithms: equal(), includes(), and mismatch()
  • generation and mutation algorithms: fill(), for_each(), generate(), and transform()
  • numeric algorithms: accumulate(), adjacent_difference(), partial_sum(), and inner_product()
  • set algorithms: set_union() and set_difference()

 

 

3.3. Operations Common to All Containers

📌A Function Definition Cover All Common Functions of Containers

 

3.4. Using the Sequential Containers

📌Initialization of Container

The following are the common ways of initialized the containers.

 

 

📌Operations supported per container

 vectorlistdeque
front()
push_front()
pop_front()
[0]
[1]
...
[n-2]
[n-1]
   
pop_back()
push_back()
back()

 

📌Different Ways Using insert()

1️⃣the insert() is like: iterator insert(iterator position, elemType value)

 

2️⃣void insert(iterator position, int count, elemType value) inserts "count" elements of value before position.

The following

 

There are many other overloaded version. Please refer to docs.

 

📌erase in STL

 

📌Linked list does not support offset arithmetic

Because the addresses of Linked List are not contiguous! Please refer to my Algorithm Repo.

 

 

3.5. Using the Generic Algorithm

📌Prerequisite

 

📌Generic Search Algorithm

FunctionDescriptionReturn
find()searches unordered collectiontrue found, false not found
binary_search()searches ordered collection, more efficient than find()true found, false not found
count()count the number of that containerint represents the number
search()matches a subsequence, e.g. find {5,7} in {1,3,5,7,2,9}the iterator of at the beginning of subsequence if found, the end if not found

 

📌Implement search element in Fibonacci sequence using generic algorithm

 

📌binary_seach() only works for sorted container

It is left to the programmer to guarantee the preceding requirement! What if we are not sure?

 

 

3.6. Design a Generic Algorithm

📌A filer function

Suppose we have a function to filter out numbers less than 10:

It is just a very simple for loop to filter out the vector. But the constraints❌ are:

  • cannot specify the value for filtering
  • cannot specify > or <

Therefore, we could implement something like this:

📌Function Objects

Definition: A function object is an instance of a class that provides an overloaded instance of the function call operator.

Analogy: Delegate in C#, Pointer to Function in C++

Example:

Suppose we have a sequence of numbers:

S1={s1,s2,,sn}

Do the element wise addition:

S1={s1+s1,s2+s2,,sn+sn}

Do the element wise multiplication:

S1={s1s1,s2s2,,snsn}

Then add to another sequence:

S1={s1,s2,,sn}S2={s1,s2,,sn}SF={s1+s1,s2+s2,,sn+sn}

We can use transform() function to do element-wise operation. It takes 5 arguments:

1️⃣ start of elements range to transform

2️⃣ end of elements range to transform

3️⃣ start position fetching data (iterator points to the beginning of container which fetch data)

4️⃣ start position apply those data (iterator points to the beginning of the container where apply those transformation)

5️⃣ function object (delegate / pointer to function) to apply those changes

 

📌Function Object Adapter

In short, the function object adapter modifies a function object by specifying lhs/rhs as input. For example,

bind1st binds the 1st operand. bind2nd binds the 2nd operand.

 

📌More General Version

The following is a really excellent example. Please digest!

The *at++ = *first++; is just the following:

 

📌A Subdivision method on vector

The following function is kind of similar to C# (where x => x< ???)

 

3.7. Using a Map

📌Regular Operation with map

The analogy of map in C++ in C# is Dict< , >.

Taking into account that the syntax query a key-value pair, first refers to key, second refers to value.

 

📌Appropriate Way Finding a Value

Suppose you want to find out if a value is in that map, you may do something like this:

It can work but it is not appropriate❌. First, words["vermeer"] return some value, if it is 0, then it is false as well. Therefore it could work. But the point is if there is no such key before, using statement like this will add the key-value pair to the map!

 

The preceding method using map.count() will return the occurrence but it will not add an empty key.✔

 

The preceding method using map.find() will return the iterator at such position.✔

 

3.8. Using a Set

You know what set is...

📌Turn a vector to set

 

📌Insert value to set

 

📌Iteration over a set

 

 

 

3.9. Iterator Inserters

📌Why should we use iterator inserter?

In short, for saving memory! Preceding methods are good but they must be required with sufficient size of such container(iterator pos). Therefore, here comes "inserter" which does not require specific size of container.

 

📌Common Iterator Inserters

Parallel to container operation, the inserter works as the following:

Container OperationIterator Operation
push_back()back_inserter()
insert()inserter()
push_front()front_inserter()

 

📌add a range of elements into vector using inserter

I think this is the primary advantage of inserters.

Instead, you can insert range like this:

 

3.10. iostream Iterators

📌Fancy Read something and Output something

We are often required to

1️⃣read something in,

2️⃣sort it,

3️⃣output it.

The preceding codes are not quite elegant. Please refer to the following:

The following is to output to a file

 

 


1 A sentinel value (also referred to as a flag value, trip value, rogue value, signal value, or dummy data)[1] is a special value in the context of an algorithm which uses its presence as a condition of termination, typically in a loop or recursive algorithm.
Previous
Previous

Clean Coder # 01. Professionalism

Next
Next

Essential C++ # 02. Procedural Programming