The STL continues...

Prerequisites

Reasonably good understanding of STL. A couple or more container classes, and iterator knowledge are sufficient.

You must have the Visual C++ 2010 compiler, or a compiler that supports the new C++ and updated STL (I don't have references to other compatible compilers; if you have, let me know).

This article explains the changes in the STL library. Like the changes in C++ languages, as explained in this article, the changes in STL are on top of TR1. Briefly, following are the advancements in the Standard Template Library:

First things first; constant iterator is not the same as const iterator. A constant iterator is const_iterator. When the keyword const is applied to an iterator (or any other variable), that variable cannot be changed further. A const_iterator (constant iterator), on the other hand, does not allow the referenced element of the container. You would like to re-read this paragraph after glimpsing the following code:

So, what's new in this?

Well, the member functions that would explicitly return Constant Iterators (i.e., const_iterator) irrespective of the context where those methods are called. Before that, I would like to let you know that iterator accessor methods (like begin, end) would return a normal or constant iterator as:

If container is constant, or the accessor is being called in the class' method which is const, const_iterator would be returned.

If, on the left side is a const_iterator type variable, the normal iterator would be returned, but would be sliced down to const_iterator (i.e., would be converted).

If, either way, the container is non-const - and no variable is declared, or the iterator variable is on the left side, it is the normal iterator.

Note: Most iterator accessor methods, like begin and end, are overloaded to return constant or non-constant iterators.

Thus, by just looking at one line, you cannot determine if a normal or constant iterator would be returned. Neither can you explicitly ask for a constant iterator:

IntVector.begin();

The new methods are:

Accessor Method

Meaning

cbegin

Returns const_iterator of the first element in the container.

cend

Gets the const_iterator to one past the last element in the container.

crbegin

Retrieves the const_iterator to the first element in the reverse iterator. Essentially, a constant iterator to the last element.

crend

Returns const_iterator to one past element in reverse iteration. Logically, the iterator before the first element in the container.

All the methods are declared const in the respective container classes.

What's the whole point of introducing all these methods?

Good question, that you should ask! When, one way or the other, you can specify which type of iterator you need, you probably don't need these new methods.

Well, the answer is: the revampedauto keyword! If you have read about the new meaning of the auto keyword, you know that you can avoid typing the type of variable being declared. The compiler can deduce the type from the expression on the right. Now when you just make a call to:

auto iter = IntVector.begin();

You cannot tell (by looking at the code) if a normal or constant iterator is being retrieved. Thus, to explicitly ask for constant iterator (const_iterator), you can use:

The constant accessor methods simply call a non-constant method (I don't guarantee, I saw in the source code). Thus, is it perfectly valid to compare with cend, instead of end; and vice-versa (disclaimer attached!).

What about sets and sets?

For all set classes (set, multiset, unordered_set, and unordered_multiset), the iterator is always constant. The means, methods to retrieve begin or end (any variant), calling find, and index operator ([]) will return a constant iterator. The data type for seemingly non-constant iterator methods like begin, find would, however, return iterator, instead of const_iterator; their behavior would be as of constant iterator.

It inserts 10 into set, and then tries to modify the content on top of the set. Since only one element is inserted, the begin call would return the iterator of the same element. It simply modifies it. On VC9 and earlier compilers, it gets compiled successfully, and the value is amended to 120.

Beginning with the VC10 compiler, the third line doesn't appease the compiler, and the compiler complains:

error C3892: 'std::**::begin' : you cannot assign to a variable that is const

For map, key modification isn't allowed, and value modification is allowed. The behavior is intact. Here is a code example:

The array is a container class which stores elements in a fixed sized array. The size of the array is given as a template argument, along with the data type. The size must be a compile time constant (like ordinary C/C++ arrays). Other than reducing or increasing the size of the container (i.e., the number of elements), it supports other standard methods - like iterating, randomly accessing, swapping, assigning etc.

Header: <array>

Namespace: tr1. But, 'using tr1::array' is implied by the header. Thus, only the std specification is sufficient.

An example:

array<int, 64> IntArray;

The first argument to template is the data-type, and the second is the compile time integer constant. The size of IntArray cannot be changed later. You can, for sure, use other data types instead of int or basic data types. Depending on the operations being performed, copy constructor, assignment operator, comparison operator may be required for a custom data type. For a custom data type (class, struct), the default constructor must be accessible.

This class is introduced to seamlessly integrate with other STL components. For example, if you use vector or list, you need to call the begin/end methods to access elements. When you decide to make that container a fixed-sized ordinary array (C/C++ array), your code would fail to compile. You need to change. By using the array class, you get the flexibility of STL and the performance of ordinary arrays.

With the latest compiler support (TR1 addition), you can also initialize an array as:

array<int, 4> ByteUnits= {1024, 2048, 4096, 8192};

When you omit one or more elements to be initialized, they would be set to zero. When you do not initialize any element of array, all elements would be uninitialized, and thus undefined (standard C/C++ rule!).

Following STL standard methods are supported by the array class:

at, operator [] - Returns the reference of the specified position.

back, front - Returns the reference to the first and last positions, respectively.

begin, cbegin, rbegin, crbegin - Returns the iterator of the first element.

end, cend, rend, crend - Returns the iterator of one past last element.

empty - Determines if the container is empty. It would return true only if the array size is zero.

size, max_size - The size of the array object. Always the same, since it is the compile time defined size of the array.

Methods that need refined discussion:

array::assign and array:fill

These methods are identical, and their task is to fill all elements of an array with the given value. They would replace all values of the array object with the specified value. For example:

This would set all ten elements with value 40, and then display all the values on the console.

array::data

This method would return the address of the first element of the array. For native arrays (say, int_array[N]), this method is the same as the expression &int_array[0] or int_array. This method is given for direct pointer manipulation. Both const and non-const methods are available. For example:

int* pArray = MyArray.data();
// Or
auto pArray = MyArray.data();

array::swap(array&) and swap(array&, array&)

Swap the contents of two exactly same size and typearray objects. The first version, which is a non-static class method, takes an argument as another array object and exchanges the content with that array. The second version is a global function, which takes two array objects (exactly the same type!) and exchanges their contents. Examples:

Any regular STL programmer would know of a struct named pair, which can hold two elements of any data type. Other than maps, it is also useful to pack two elements, instead of defining a struct for them. The first and second member variables are actually those packed values. Just to recollect and for the sake of relevant discussion:

Programmers often typedef them, so that variables can be declared easily and pair can be passed elegantly to functions.

What if you need to pack more than two elements?

Well, in that case, you probably would define a struct, or use multiple pairing. The former approach doesn't allow seamless integration with STL, and the latter does not allow elegant and easy coding.

Here is a boon for you: the tuple class. The tuple class allows 2 to 10 elements to be put together. All types can be different, and depending on the desired operations, special methods from your class would be demanded. "How" tuple class works for template overloads is beyond my comprehension. I would discuss "what" can be achieved, and "where" it can be used.

Header: <tuple>

Namespace: tr1. 'using tr1::tuple' is implied under std.

Let's start with the examples:

Declaration of a two-element tuple:

tuple<int,int> TwoElements;

Initialization at construction:

tuple<int,int> TwoElement (400, 800);

Initialization of all elements (if being initialized) is mandatory. The following is an error:

tuple<int,int> TwoElement (400); // Second initializer missing

The error would not be straightforward, but you can understand that initialization of all elements is required!

Initialization after construction - it is not as simple! Not as elusive though. If you remember, there is a make_pair helper for pair construction/initialization. You got it right! The helper function make_tuple is for you. If you have read, or will be reading about Concurrency Runtime in VC++ 2010, you would find that there are other new make_ functions in the C++ library.

Anyway, here is how you initialize a tuple object:

TwoElement = make_tuple(400, 800);

Like the tuple template class, make_tuple is also overloaded to take 2 to 10 parameters. It is interesting to know that make_tuple utilizes R-Value reference concepts of the C++0x language, and the perfect forwarding feature of STL. The former is discussed in this article, and the latter will be discussed later. In short, they allow the same object to be re-used instead of calling copy constructor and assignment operators, thus improving overall performance.

You can use make_tuple along with the auto keyword to smartly define a tuple whose type would be deduced by the compiler:

Moving ahead. Those were the few ways to initialize a tuple object. Now the obvious question that would arise would be "How to access elements of a tuple?".

For a pair object, we can simply use its templatized member variables: first and second. But, since the tuple class does not define any named member variables as such, we need to use the helper function get to access those unnamed variables. Example:

The explicit template parameter, which would be an integer (specifically, size_t), specifies the zero-based index. The index must be within the range of the tuple's number of template arguments. In this case, it must be 0, 1, or 2. In the above example, the get function is used to retrieve the second element of the tuple object. The second element is an integer, which is being stored in the nAge variable.

If you specify out of index to the get function, the compiler will not be happy. It must be a compile time constant and within range. Also, the return type is also deduced at compile time; you cannot fool the compiler by:

The tie function takes and returns a reference to a tuple object; make_tuple logically creates an object, and takes/returns a non-reference. The last line would compile, but does not give the expected results, since all three parameters are passed by value, and an attempt to modify the temporary tuple is being made. I recommend you to use the tie and make_tuple helper functions for what they are designed.

The six relational operators

Like array, all six relational operators are defined for the tuple class also. All overloaded operators demand that both operands (tuple objects) must be exactly of the same type! Following C/C+ semantics, the functions return true or false as the result.

The following functions are new to the <algorithm> header as part of the TR1 additions:

all_of

any_of

none_of

find_if_not

copy_if

partition_copy

is_partitioned

partition_point

minmax_element

is_sorted

is_sorted_until

is_heap

is_heap_until

move

A basic understanding of how algorithm functions work is expected from the reader. Like how the count, count_if, or find_if functions work. In short, they work on the given iterator(s), and return the desired result. Like count_if counts the number of elements in a given container (specified by iterators) that match a specified criteria. Most functions do not change the given ranges. A few functions do modify one of the given ranges (like copy, remove_copy), and a few functions modify the given range (for_each, fill, generate).

all_of function

The function all_of tests all elements in the given range and returns true only if all elements match the given predicate. If any element fails to match the predicate, the function aborts further search and returns false. Example:

The code above fills all 10 elements of the vector with the value 64. It then calls the all_of function to test if all the elements are even numbers. The predicate is provided via the C++0x lambda. It can be a function pointer or a function object (functor). Here, it would return true, since all elements are even. If you uncomment the commented (third) line, the return value would be false.

For this function, and all forthcoming functions, it is important to note that the given function does not copy the given functor/lambda. By that I mean: the given predicate would be called for each iteration, and the behavior depends on what the predicate is returning on that call. Let me explicate through an example:

With stateful lambda, the meaning of the predicate function has changed. Therefore, with each call, the function would behave differently. For the first call, it would test with modulo 2, then with 3, and so on. Thus, you see that the given predicate function is not copied by the algorithm function, but called on each iteration. Though this behavior may be desirous for you - do make a note of it!

any_of function

You can guess the behavior of this function; it tests if any element matches the given predicate, and if so, returns true. If no element matches the given predicate, it would return false. It is worth noting that the function would exit as soon as it finds an item matching the given predicate. This function is not perfectly the same as the find function. The find/find_if function returns an iterator, but any_of returns bool. Many times, we just need to locate if an item exists in a container or not. Using 'iter != container.end()' is cumbersome. So, here is a boon for you:

It would return true, since number 3 exists in the container. To find if any even/odd number exists in the container, you can change it appropriately. Here is another example, which finds a country name in a given list of strings:

If you want to know which element has matched the criteria, you need to use the find or find_if function. Unfortunately, the any_of function doesn't support searching with a given value; it needs a function/lambda!

none_of function

The none_of function returns true if no element matches the given predicate. Semantically, it is same as !any_of. It returns true only if all elements fail to match the criteria (i.e., the predicate always returns false). If any match succeeds, it returns false. Example:

The variable bLessThan2K would be true since no item is more than 2048. When you uncomment the second line, the return value of none_of would be false, since it finds a match failing the predicate. You achieve the same by replacing 'none_of' with '!any_of'.

find_if_not function

Similar to the find/find_if functions, it returns the iterator of the appropriate type. It attempts to locate the first item which does not match the given predicate. For example, you would like to find a person who is not married. Since marital status can be multiple, it is better to find if someone is not married (a search for 'unmarried' may not do good!). Let's see an example:

The code above demands you to revisit the tuple! The type of iter is vector<Person>::const_iterator, which is shortened via the auto keyword.

copy_if function

This function demands some attention and explanation. First, the root function: copy, which copies a range (input) into another range (output). Since, this is a copy function, and not insert, the destination range must have enough space. Example:

Moving ahead, for a relevant discussion. The remove_copy function can be used to copy only elements that match the given value to another container/range. Further, remove_copy_if can be used to copy by a given predicate. The "remove" word in the function name does not mean that it would remove something from the original range, but copying only values that match value/predicate, removing (excluding) others. The requirement of the target range is the same as the copy function. Here is an example for both:

Now, if we need to copy only even numbers, we just need to change the expression in lambda as:

return nValue % 2 != 0;

You see, to put something you need, you need to express in "don't need" form. If the condition involves multiple conditions (AND, ORs), it becomes more complex to write and understand. Yes, the solution is to negate (!) the whole condition. But that approach again requires you to write in "don't need" form, and is elusive to understand.

The copy_if function provides a clean and direct approach. You can say it is a copy function with a condition test. The following is code for the "I need events only" requirement:

Note: As of this writing, the MSDN documentation of copy_if stands incorrect. It says it needs three arguments, but actually it takes four arguments.

partition_copy function

The partition_copy function partitions the input range into two different output ranges, depending on the condition. If condition is true, it copies into the first output range. If condition is false, it copies into the second output range. Here is an example which generates a few random numbers, and then copies even-numbers and odd-numbers in different vectors:

This function tests if the given range is partitioned based on the condition given. For example, an array of 10 elements may have a set of negative and zero/positive numbers. Further, assume all negatives are placed before positives, and all positives follow negatives. In that case, it is said to be partitioned. The function is_partitioned would return true if the range is properly partitioned; otherwise; it returns false.

Documentation says it differently, but by working and looking at the source code, I found out this is the same as calling find_if_not. Thus, I would not write about it until I get the correct information about this function.

minmax_element function

The minmax_element combines the work performed by the min_element and max_element functions. A recap for you: min_element finds the smallest element in a given range, and max_element finds the largest value in a given range. Since they return an iterator, and not a value, the first found iterator of relevant occurrence would be returned.

The minmax_element returns a pair of type pair<iterator,iterator>, the first iterator being a reference to the minimum element and the second being a reference to the largest element. Other than just avoiding two function calls, it also performs better than two combined. It performs faster: it finds both values in N * 1.5 attempts, as opposed to N * 2 attempts the two function calls would take (N is the number of elements in a given range).

All three functions are overloaded to take 2 or 3 arguments. The 3-argument function takes a predicate to test for. The predicates should logically implement less-than comparison.

is_sorted function

As the name suggests, the function is_sorted determines if the given range is sorted ascending (tested via less than operator), or as specified by the predicate function. If the range is sorted, it returns true, otherwise it returns false. The following example is clear-cut, and needs no further explanation.

If we need to test if a vector of Person tuple is sorted or not, we must pass the binary predicate as the comparer, since tuple doesn't define operator< to assist it. In the following example, our "is vector sorted" is based on the person's age.

In case you have some other type of object (like struct, class, pointers), you must provide the appropriate comparer as predicate. Since this article is about new concepts in STL, I am not attempting to give standard examples.

is_sorted_until function

The big brother of the is_sorted function. The is_sorted_until function is called by is_sorted. The actual task is performed by is_sorted_until.

What's the difference, you might ask! Well, only that, is_sorted returns bool, whereas is_sorted_until returns the iterator, afterwhich the sorting test fails. For a range [First, Last], if the sorting test succeeds, it returns Last (i.e., the end of the given range); otherwise, it returns one of the iterators where the sorting-test failed. For example:

About STL heaps

Frankly, I did not know what an STL heap data structure is. A search did not give me enough results, so I assumed it was unknown among STL programmers. After I studied and practically tried heaps, I found that heaps are not useful for most program designs. Still, for completeness (is_heap, is_heap_until function elaboration), I need to explicate what a heap is, how it is created, and what possible uses it has.

In STL parlance, heap is not a data-structure by itself - there is no STL class relegated for it. An STL heap works on top of a random access container (like deque, vector, array, native-arrays). There are a set of functions for making a random-accessed container a heap.

Note: A heap is not the same as heap-memory. A heap, in Computer Science, is a data structure!

In the heap data-structure, the largest element always remains on top. The rest of the elements are placed so that they form a (specialized) binary tree (say binary heap). Unlike, the binary-tree data structure, which requires pointers, and left and right nodes to hold data, the heap is generally implemented through an array. Also, in heap, there is no sorting (when iterated in Root-Left-Right manner); only that, elements are placed so that the root element is the largest among its children (the same rule for all underlying nodes). Before I discuss more, let's see a binary heap example:

{ 500, 40, 300 };

Which is a valid binary heap:

500
40 300

Let's have more elements in the binary heap:

{ 4096, 512, 1024, 16, 32, 128, 4};

Which can be represented as:

4096
512 1024
16 32 128 4

And finally a tree, with 10 elements, which would form a non-strict binary tree:

10
9 7
8 5 6 3
1 4 2

Thus, you see that a binary heap has the following properties:

Largest element is always on top (which is a max-heap; min-heap would have the smallest on top). For each level of tree, the same rule applies.

The elements are not sorted, though they appear to be sorted. Neither is it a binary tree.

For 2^(N-1) elements, the binary-tree is fully filled. Otherwise, items on lowest level are placed on the left of the tree.

Heaps can be used to implement a priority queue (in reality, priority_queue is based on heaps and the STL functions given below). I am unaware of other useful implementations.

Creation of heap, and other heap operations

For any randomly accessed container, the STL function make_heap can be used to turn it into a heap. For example:

This would make IntArray a heap (exactly as shown in the last binary heap). Further, pop_heap can be used to retrieve the largest element from the heap (and logicallyremove it). The pop_heap function removes the largest element, and puts it into the end of the heap, and re-forms the heap (excluding the item removed). Example:

It is important to know that the heap by itself is now of nine elements only. Thus, it is the programmer's responsibility to remove that item from the heap. Since IntArray is an array, no item can be removed. For this, we can use a counter, and use IntArray.end() - nCounter for heap operations. For vector and deque, the method pop_back can be used.

To insert an item into a heap, the push_heap function can be used. Before that, the actual element must be pushed into the relevant container. For example:

It's on you how you manipulate heaps - if you pop-the-heap first and then get the last element; or if you get the first element and then pop it from the heap (followed by the removal of the actual element, in both cases).

The last function, just for completeness, is the sort_heap function which will sort the heap. After the heap has been sorted, it is actually a sorted container, and is now no longer a heap.

In a nutshell, about STL heaps:

Based on random access container.

Call make_heap to initially convert a range into a heap.

Access front-element to get the largest item in a container.

Call pop_heap to logically remove it from a container, andthen actually remove it from the heap. Call the appropriate function to remove it physically from the container.

Use push_heap, followed by the actual item insertion, to re-form heap.

Convert the heap to sorted range using the sort_heap function.

Back on track! Let's discuss about the new STL functions, on heap.

is_heap function

This function determines if the given range is a valid heap or not. Return value is true, if the range is a valid heap; otherwise false. For example:

Just like the is_sorted function, another overload of this function also takes a binary predicate as comparer. The heap should have been created using a similar predicate (i.e., via three argument versions of make_heap, push_heap, and pop_heap).

is_heap_until function

Similar to the is_sorted and is_sorted_until relationship, this function returns an iterator instead of bool. The returned value can be used to test where the heap-test failed. The is_heap function calls this function, same as is_sorted calls is_sorted_until.

This function also takes a binary predicate, and it is required that the heap should be created with a similar binary predicate.

The move function

Ah! The C++0x move semantics, STL's perfect forwarding, and the variants of std::move functions... Since move has its different variants in multiple headers, and it also requires how STL utilizes R-value references for performance improvements - discussing all these deserves a different section. Thus, I would cover all these topics later in this article.

In short, move functions call the move constructor and move assignment operator, wherever applicable and possible, to move one resource to another, without creating new copy and deleting the old copy.

Most C++ programmers do not like the rand function and need to fiddle around to get a good random number generator, and with large variations. Well, STL guys have listened and prepared a brand new header for us: <random>. This header facilitates dozens of classes to get random numbers - of different data types: bool, integer, and float/doubles!

Before I attempt to explain the concepts about this header, which would probably twiddle your brainbox, let me start with a naive example:

As you can see, it generates 20 random numbers and displays them on screen. The type default_random_engine, which is actually a typedef to another class, generates random numbers for you. operator() returns the next random number. The code above would result in something like:

The values returned are in the range of signed long (4-byte signed integer).

Both positive and negative values are returned.

Now, let's not further disgrace our old friend rand. The <random> header has much more for you, which cannot be compared with the standard random generator functions.

If you run the code above multiple times, it will give the same set of results. To get different values, you need to use the seed method. Though, seed method has three variants, I exemplify one which takes an integer:

// Putting it before the loop.
def_rand.seed(45001); // Takes 'unsigned long', must not be (less than) zero

This would now generate a different set of numbers. Yes, with the same seed value, the program would still generate the same set of random numbers. Not to worry, the header now has much to offer.

And now the concepts and terms!

The following are logical divisions of classes in the <random> header:

Random Number Generator is an object that produces random numbers. Simply put, an instance variable (def_rand for the above example). This has a specific meaning since it can be combined with the class types mentioned below.

An Engine is the generator class, which actually produces random numbers. An engine can be a Simple Engine, which generates random numbers by itself; or it can be a Compound Engine, which utilizes one or more other simple engines to generate the final random number. Furthermore, an engine can be combined with a Distribution. (The default_random_engine, is one type of engine.)

A Distribution is a class which generates uniformly distributed random values. A distributor utilizes an engine to generate random values.

All engines, except random_device_engine, generate pseudo-random values - which means those engines would produce the same results if unseeded; or if seeded, with the same seed value. Thus, it may be required to utilize random_device_class to seed a random value for the appropriate engine. An example:

random_device rnd_device;
def_rand.seed(rnd_device());

Which causes the def_rand (i.e., default_random_engine) to produce a different set of random values on each run. More about this class soon.

For progressive continuation, allow me to defer class-classifications, and let me present more examples. Now, instead of utilizing arbitrary random values, you would need values to be in a given range. For example, you can do modulo with 100 (and plus one, if you need) to get values within a range:

which would produce values only in the range 0 to 99. This is where distribution classes can be utilized! For this scenario, you can use the uniform_int_distribution class. The following code generates true random values in the 1 to 100 range:

uniform_int_distribution is one of many distributions. On construction, I have setup it to emit out only values in 1 to 100. The random_device class is used to seed the def_rand object, which would facilitate it to produce truly random values on each run. Further, with the expression uni_dist(def_rand), the engine is combined with the distributor. The distributor calls the operator() of the given engine.

Don't count! A probability is a probability, not a necessity. If you need 70% false probability, just change 0.7 to 0.3!

Okay. Till now, you had two Simple engines: default_random_engine typedef and random_device class; and two Distribution classes: uniform_int_distribution and bernoulli_distribution (ignore the meaning!). You see that the Engine actually generates the pseudo-random value, and Distribution filters it appropriately.

Properties of Engine classes

Every engine has the following properties:

Property

Meaning

Argument Type

The class template argument type. Can be one, two, or none; depending on input and/or result types taken by the engine template class.

If none, the engine class is not a template class, and result_type is explicitly defined by the class (like random_device, which has int as the result_type).

result_type

The data-type which is returned by operator() for the class. This is the first argument type, if any, taken by the template argument.

This type is returned by the min and max methods of the class.

One version of seed takes this type as its argument.

min and max methods

Returns the minimum and maximum value that can be returned by operator() of this engine class.

These values may be predefined, or are dependent on the template arguments given.

seed method

Seeds the engine for the next random values.

The seed method can take integer/real value to seed (actual type depends on the engine class).

It also takes the seed_seq class (described later).

Another overload also takes functor/lambda, which is called multiple times to generate a seed.

Not available for the random_device class.

operator()

The operator which returns the next random value.

The return type is result_type.

Returns in range between min and max.

discard method

Calls operator() the given number of times, effectively discarding the set of the generated random values.

I did not want to write so much of theory and reference material, but I needed to do; since there is no good understandable text available, as of now. So bear with me! The tables and text shown may seem alien now, but you would need to refer to them again, and they would prove to be beneficial.

The example shown above is just for illustrating Engine properties, not all class(es) shown have been discussed. The example shown above also exemplifies the compound engine class, which is yet to be elaborated.

For all engine classes and for all distribution classes, the result type and template argument type can be one of the following (these are not typedefs or enums, but just template parameter placeholders in the documentation/header).

IntType indicates class takes either signed or unsigned type.

UIntType states that class takes unsigned integral type. For both types, integral would mean bool to __int64 as base integer type.

RealType indicates float or double type.

Properties of Distribution classes

There are more number of distribution classes (around 20) than there are engine classes (around 8). Therefore, unfortunately for readers, there are more properties applicable for distribution classes. The properties that are same as engine classes are clubbed together in the first row of the following table:

Property

Meaning

Argument TypeResult Typemin and max,operator()

All distribution classes have these properties/methods.

The seed method is not applicable for distributions.

The operator() takes engine, which would generate the pseudo-random value.

input_type

The type that engine's operator() should return.

The compile would convert types, else it would give warning/error.

This type is either predefined by the distribution class, or specified by the template argument.

operator()

Takes an engine object as argument.

Calls operator() on that engine

Distributes (filters) the returned value as per distribution, and returns value to caller.

Another overload takes param_type as the argument.

reset()

Resets any cached values, so that the next call to operator() does not depend on the previously obtained values.

param_type

Internal type, which is beyond the discussion.

Pre-Condition

Precondition as specified by the distribution that must be met, while constructing the object.

The debug build simply calls the assert function, if pre-condition is not met.

An example would be 0.0 to 0.5 as precondition, for the distribution argument (recollect 0.7 ?).

Okay, let me try to explicate things, by giving more of examples and their outputs. I will not explore all classes in the <random> header, but a few important/interesting ones. At the end of this section, there is (would be) a table of all classes and their salient features. Believe me, gathering relevant information, producing good examples, and writing it down is far difficult than the difficultly level of reading and understanding it!

Let's use the class that implements the oldest and best-known random number generator, Linear Congruential Generator. This algorithm is used by many runtimes, and is used by the rand function. [Need not understand the following]. The LCG algorithm generates the next number, as defined by the following formula:

X[i+1] = (A * X[i] + C) % M; // x(i+1) = (a * x(i) + C) mod m

Where A, C, and M are integral constants. The constants A and C must be less than M. The engine class (Simple Engine) for this is linear_congruential_engine. This is the class template, whose first argument takes UIntType and the rest of the arguments take unsigned integer constants that reflect A, C, and M. The prototype of the class template is:

As pointed out in Properties of Engine classes above, this is one of the engine classes which takes the data-type and that data type becomes the result_type. Therefore, result_type for this instance (lce), is int. This is one of the Simple Engines.

We have used the default engine, named default_random_engine, which is actually type-defined with mt19937:

typedef mt19937 default_random_engine;

What's that? Well, mt19937 is type-defined with the template instance of mersenne_twister. Another type-definition for 64-bit Mersenne Twister is expressed as mt19937_64, which is the template instance of the class mersenne_twister_engine with a 64-bit data type (result type).

Don't get it? Don't bother! I just mentioned it for completeness, since default_random_engine is used in this article, and you'd probably use it for quick code. Also, in MSDN documentation and examples, mt19937 is used frequently. mersenne_twister(_engine) is, again, a simple engine.

Note: All VC10 engine classes end with _engine. If not, they are not new to VC10.

Now, let me explain a few Compound Engines, the engines that take another engine to act upon.

If you remember a common method for all engines named discard, you would recollect that it effectively discards a given number of next random values. It does so by calling operator() for the same class. What if you need to attach something with an engine that discards N random numbers, and gives the next random numbers? For this, you can use the Compound Engine named discard_block_engine. This template class takes another engine, and some constant numbers - using that, it shells out random values. Here is an example:

The object dbe, of type discard_block_engine, is instantiated, taking default_random_engine as its parent engine. The random values would be generated by the parent engine only, and discard_block_engine will just discard and return a set of randoms depending on the template parameters. The second template argument (integer) specifies the total block size (specified as P), and the third template argument specifies the used block size (R).

The engine produces R (2, as per code above) random values, and after that count, it ignores (discards) P - R (3) values. For example, if the original numbers generated as 1 to 20, it will produce:

1, 2, 6, 7, 11, 12, 16, 17...

For this program, which is based on default_random_engine (Mersenne Twister), the result would be as follows:

The modulo with 100 is done just to produce small numbers, so that it can be analyzed easily.

Well... The random discussion is getting long and long, and I am postponing further discussion of different classes. If time allows, I would definitely do more research and update the gained information here.

First thing first, about sets. In STL, a set is a data structure that implements an associative data container. It holds only unique values, discarding any duplicate insertions. It stores them in ordered form, meaning that when you iterate over the set, the values would be ascending sorted (with the default comparison function). The following example inserts some random values, and then displays them:

Advancements in sets

New classes, named unordered_set and unordered_multiset, and their new special methods.

You see that set always puts elements in ordered sequence. Thus, the insertion is costly (O(log n) approximately) - and, as you might understand by "Big-O" notation, that insertion time complexity increases as the number of elements increases. Anyway, I am not here to put these jargons. Let me put it short, a set keeps only unique elements, and in sorted ordered. Insertion, Deletion, and Searching take the same time: O(log n).

With TR1, a few compilers, including Visual C++, added an unordered set named unordered_set, which keeps only unique elements, but not in any specific order. It puts elements in a set of buckets with the help of a hash function.

Header for unordered sets: <unordered_set>

Note: Before TR1, the unorderedness of sets were available through the hash_set and hash_multiset classes. These classes, however, did not have all the features that are in the new classes.

The same collection of values, no duplicates - but not in any given sequence. The logical question you should ask, "Is unordered_set faster than set?". Well, yes! The insertion time is almost the same (expert comments needed), but unordered_set would locate the element faster than set. See this article for performance benchmarking. The following code inserts elements into a set, then searches half of them:

These are a total of 100 numbers (and not just 60 numbers), since multi-sets allow non-unique numbers. You also see that unlike multiset, which stores elements in sorted order, the unordered_multiset doesn't adhere to sorting. But, a close analysis of numbers shown above (which is just the begin to end iteration) reveals that similar numbers are grouped together. This is where buckets and hash-functions play and show their role. Remember, buckets and hash-functions also play a role in unordered sets; though it is not explained yet.

Likewise, when you change from multiset to unordered_multiset in SET2.CPP, it would put numbers in buckets. Since numbers were few and already inserted in order (0 to 3), the result doesn't differ (see SET2.CPP's output). But for other sets of values and insertion order, multiset and unordered multiset would definitely differ, since unordered set doesn't put elements in sorted order.

Unordered sets and maps use buckets and hash-functions to store elements. There are a set of functions to see how they are performing, how many buckets are there, and how many elements there are in the buckets, and the load order of the buckets. You can also ask to rearrange the buckets (re-hash). It is also possible to use your own function as the hash-function. Since these concepts are common to both unordered sets and unordered maps, I would explicate them after the unordered maps discussion below.

For the reader who has no idea or has a foggy idea about STL maps, let me brief. A map stores a key and its value, and the key must be unique. With set, the key is the value (i.e., both are same). The value can be duplicate, but key cannot. In a students-collection, the roll-number can act as key (roll-numbers are unique), and the student's name can be the value. The following example clears out some fog:

Just like unordered_multiset's scatteredness, the elements are placed into different buckets, thus iterating through doesn't give the proper order. It should, however, be noted that the values of the same keys are put together (expert comment expected).

Okay, let's have another code that would show us the performance statistics of all four map classes:

The timings shown are only indicative. On multiple runs, I have found map and multimap to always behave as shown. But, many times, unordered_map reached a searching speed the same as unordered_multimap, though it never surpassed an unordered multimap's speed.

Since this article is not about introduction to maps, I did not write about it, but in brief. For multimaps, lower_bound and upper_bound should be used to iterate for a given key, since multimaps store more than one value on a given key. For multimaps, the find method would always return an iterator to the first element in the given key. The method count can be used to find out how many elements are in a given key.

Hash, Buckets, and More...

This subsection explores the buckets, hash-function, and comparison functions applicable for all four unordered containers. All these concepts are similar for all containers, and if they differ, I would explicitly mention. Let's start with simple operations, and then we will dive into the details.

The following typedef and the declared variable would be used for this subsection, and I would mention whenever I change the typedef.

typedef unordered_set<int> Container;
Container cont;

Let's see the default count of buckets:

wcout << cont.bucket_count() << "\n";

Which would return 8, for all four types of unordered containers (MS implementation, others may vary). This means, there are a total of eight buckets in which elements would be placed. What about the element count in each bucket? Well, we can use max_load_factor to find that:

Which means there are now 64 buckets, and each bucket can (still) hold only 1 element. If we had just inserted 8 (or less) elements, the bucket-count would have remained the same (8). Likewise, when we insert 65 or more elements, the bucket-count changes to 512, and so on (still, each bucket would hold only one element). Since there can be less number of elements than buckets, not all buckets would be filled in (irrespective of bucket-size); a few buckets may have zero elements.

A few questions should arise:

How do you change the bucket size?

How to know which bucket has how many elements in it?

What is the average number of elements in all buckets (known as load-factor)?

What is the situation of all buckets (i.e., hash-table) after elements are deleted and inserted?

The manageability of buckets depend on the load-factor. You use another overload max_load_factor which sets the load-factor. It simply specifies the maximum number of elements to be put into a bucket. Thus, specifying a larger value would simply mean less buckets, since a bucket can now hold more values. For example:

cont.max_load_factor(500);

asks the hash-table to put a maximum of 500 elements in each bucket. Thus, when you insert 500 or less elements, only one bucket would be utilized - no! Remember, a load-factor is just the average of the number of elements in each bucket. You are just suggesting the container to utilize each bucket to hold more (or less) elements in each bucket. An example:

The bucket count is 8, since these minimum buckets (each of capacity 100) are enough to carry all 150 elements. If you insert 800 or more elements, the number of buckets would raise to 64 (re-read the logic above). The following code, shows which bucket has how many elements:

You see that the buckets are optimally filled up. Interestingly, with the same 150 values, all four unordered containers place the elements the same way.

You might be willing to know which elements are kept by a given bucket. Well, that's easy - you just need to use local iterators; unique type of iterators for unordered classes. They are supported via begin and end overloads. There are no cbegin/cend methods, since the type [const_]local_iterator represents only constant iterators. The following code iterates through bucket 0:

Since the hashing scheme is the same, all four containers would depict the same values in the same sequence.

One last thing, in the same radius, is to find out on which bucket a particular element is put in. This is achieved through the method bucket. It returns a zero-based bucket position in which the element is put in (or not put in). Yes, using hashing, it returns the correct bucket number for a non-existent element! For example, with output:

By setting maximum load factors and inserting elements, you see that buckets count do increase appropriately. What if you want to set the bucket count yourself? Like instead of growing on its own from 8,64, 512, 4096..., you might want to give it a hint. Well, you can do it by simply calling the rehash method. This method can be called anytime, before inserting elements, after all elements have been inserted, midst of inserts, or after elements have been removed. The method rehash always rebuilds the hash-table. It will set the minimum hash size to 8, and then gradually double it until your request can be satisfied. For instance, if you specify 10, it will set to 16; if you specify 100, it would set size to 128. Remember, the max-load-factor still remains in effect.

Each bucket would have a maximum of 100 elements, a total of 32 buckets, and around 78 elements in each bucket. When you add 5000 elements in it, it would exceed the load-factor (higher than 100), and thus the bucket count would increase to 256.

Sounds confusing? Remember, when the load-factor exceeds its limit, the hash-table would add more buckets, by 8x. That means, for the above example, 32 * 8 = 256. Had you specified 10 (i.e., 16) as the bucket size, the increment of buckets would go like this: 128, 1024, 8192, and so on. It totally depends on the given load factor.

Most of us are not edison, C++ is not invented by us, so as STL, Boost etc.
But learning the existing knowledge is not easy, I think we should say thanks to
whom spent time and engergy to investigate/clearify and finally share it with us.

BTW, C++ experts such as Scott meyers also does not tell us new things, he categorize and
extract most useful things and share with the C++ community. Do you vote 1 to "Effective C++"
series just because there's nothing new?