ESP32 FreeRTOS Tasks

API

Tasks

The main job of all operating systems is to run and coordinate user tasks. Like many operating systems, the basic unit of work in FreeRTOS is the task. FreeRTOS uses a Task Control Block (TCB) to represent each task.

Task Control Block (TCB)

The TCB is defined in tasks.c like this:

typedef struct tskTaskControlBlock
{
  volatile portSTACK_TYPE *pxTopOfStack;                  /* Points to the location of
                                                             the last item placed on 
                                                             the tasks stack.  THIS 
                                                             MUST BE THE FIRST MEMBER 
                                                             OF THE STRUCT. */
                                                         
  xListItem    xGenericListItem;                          /* List item used to place 
                                                             the TCB in ready and 
                                                             blocked queues. */
  xListItem    xEventListItem;                            /* List item used to place 
                                                             the TCB in event lists.*/
  unsigned portBASE_TYPE uxPriority;                      /* The priority of the task
                                                             where 0 is the lowest 
                                                             priority. */
  portSTACK_TYPE *pxStack;                                /* Points to the start of 
                                                             the stack. */
  signed char    pcTaskName[ configMAX_TASK_NAME_LEN ];   /* Descriptive name given 
                                                             to the task when created.
                                                             Facilitates debugging 
                                                             only. */

  #if ( portSTACK_GROWTH > 0 )
    portSTACK_TYPE *pxEndOfStack;                         /* Used for stack overflow 
                                                             checking on architectures
                                                             where the stack grows up
                                                             from low memory. */
  #endif

  #if ( configUSE_MUTEXES == 1 )
    unsigned portBASE_TYPE uxBasePriority;                /* The priority last 
                                                             assigned to the task - 
                                                             used by the priority 
                                                             inheritance mechanism. */
  #endif

} tskTCB;


The TCB stores the address of the stack start address in pxStack and the current top of stack in pxTopOfStack. It also stores a pointer to the end of the stack in pxEndOfStack to check for stack overflow if the stack grows “up” to higher addresses. If the stack grows “down” to lower addresses then stack overflow is checked by comparing the current top of stack against the start of stack memory in pxStack.

The TCB stores the initial priority of the task in uxPriority and uxBasePriority. A task is given a priority when it is created, and a task’s priority can be changed. If FreeRTOS implements priority inheritance then it uses uxBasePriority to remember the original priority while the task is temporarily elevated to the “inherited” priority. (See the discussion about mutexes below for more on priority inheritance.)

Each task has two list items for use in FreeRTOS’s various scheduling lists. When a task is inserted into a list FreeRTOS doesn’t insert a pointer directly to the TCB. Instead, it inserts a pointer to either the TCB’s xGenericListItem or xEventListItem. These xListItem variables let the FreeRTOS lists be smarter than if they merely held a pointer to the TCB. We’ll see an example of this when we discuss lists later.

A task can be in one of four states: running, ready to run, suspended, or blocked. You might expect each task to have a variable that tells FreeRTOS what state it’s in, but it doesn’t. Instead, FreeRTOS tracks task state implicitly by putting tasks in the appropriate list: ready list, suspended list, etc. The presence of a task in a particular list indicates the task’s state. As a task changes from one state to another, FreeRTOS simply moves it from one list to another.

xTaskCreatePinnedToCore

Create a new task with a specified affinity.
This function is similar to xTaskCreate, but allows setting task affinity in SMP system.
Return
pdPASS if the task was successfully created and added to a ready list, otherwise an error code defined in the file projdefs.h
Parameters
pvTaskCode: Pointer to the task entry function. Tasks must be implemented to never return (i.e. continuous loop).
pcName: A descriptive name for the task. This is mainly used to facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN – default is 16.
usStackDepth: The size of the task stack specified as the number of bytes.
pvParameters: Pointer that will be used as the parameter for the task being created.
uxPriority: The priority at which the task should run. Systems that include MPU support can optionally create tasks in a privileged (system) mode by setting bit portPRIVILEGE_BIT of the priority parameter. For example, to create a privileged task at priority 2 the uxPriority parameter should be set to ( 2 | portPRIVILEGE_BIT ).
pvCreatedTask: Used to pass back a handle by which the created task can be referenced.
xCoreID: If the value is tskNO_AFFINITY, the created task is not pinned to any CPU, and the scheduler can run it on any core available. Values 0 or 1 indicate the index number of the CPU which the task should be pinned to. Specifying values larger than (portNUM_PROCESSORS – 1) will cause the function to fail.

xTaskCreate

similar to xTaskCreatePinnedToCore but it will work with core 0 like main loop function
Create a new task and add it to the list of tasks that are ready to run.
Internally, within the FreeRTOS implementation, tasks use two blocks of memory. The first block is used to hold the task’s data structures. The second block is used by the task as its stack. If a task is created using xTaskCreate() then both blocks of memory are automatically dynamically allocated inside the xTaskCreate() function. (see http://www.freertos.org/a00111.html). If a task is created using xTaskCreateStatic() then the application writer must provide the required memory. xTaskCreateStatic() therefore allows a task to be created without using any dynamic memory allocation.

vTaskDelete

Remove a task from the RTOS real time kernel’s management.
The task being deleted will be removed from all ready, blocked, suspended and event lists.
INCLUDE_vTaskDelete must be defined as 1 for this function to be available. See the configuration section for more information.
The idle task is responsible for freeing the kernel allocated memory from tasks that have been deleted. It is therefore important that the idle task is not starved of microcontroller processing time if your application makes any calls to vTaskDelete ().
Memory allocated by the task code is not automatically freed, and should be freed before the task is deleted.

create two tasks with different priorities

xTaskHandle task,task2;

void taskFunction(void* parameter){
  for(;;){
    Serial.println("task 1");
    delay(1000);
  }
  vTaskDelete(task); //delete task once you break the loop dont forget to free the memory
}
void taskFunction2(void* parameter){
  for(;;){
    Serial.println("task 2");
    delay(1000);
  }
  vTaskDelete(task);
}

void setup(){
  Serial.begin(9600);
  xTaskCreate(taskFunction,"task1",2048,nullptr,1,&task);
  //at first time resume first but because it has lower priority 
    in the next Time it will be resumed after task2
  xTaskCreate(taskFunction2,"task2",2048,nullptr,2,&task2);
}

void loop(){
}

xTaskCreatePinnedToCore
if value of core is tskNO_AFFINITY, the created task is not pinned to any CPU, and the scheduler can run it on any core available

void taskFunction(void* parameter){
  for(;;){
    Serial.println(xPortGetCoreID());
    vTaskDelay(1000);//similar to delay with arduino
  }
  vTaskDelete(task);
}

void setup(){
  Serial.begin(9600);
  if(xTaskCreatePinnedToCore(taskFunction,"task1",2048,nullptr,2,&task,0)){
    Serial.println("task created and schedualed with ready list ");
  }
}

vTaskDelay and vTaskDelayUntil
vTaskDelay () will cause a task to block for the specified number of ticks from the time vTaskDelay () is called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed execution frequency as the time between a task starting to execute and that task calling vTaskDelay () may not be fixed [the task may take a different path though the code between calls, or may get interrupted or preempted a different number of times each time it executes].
Whereas vTaskDelay () specifies a wake time relative to the time at which the function is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to unblock.

void vTaskFunction( void * pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 10;

    // Initialise the xLastWakeTime variable with the current time.
    xLastWakeTime = xTaskGetTickCount ();
    for( ;; )
    {
        // Wait for the next cycle.
        vTaskDelayUntil( &xLastWakeTime, xFrequency );

        // Perform action here.
    }
}

eTaskGetState
eRunning = 0
A task is querying the state of itself, so must be running.
eReady
The task being queried is in a read or pending ready list.
eBlocked
The task being queried is in the Blocked state.
eSuspended
The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out.
eDeleted
The task being queried has been deleted, but its TCB(task control block) has not yet been freed.

vTaskSuspend,vTaskResume

void vAFunction( void )
{
TaskHandle_t xHandle;

 // Create a task, storing the handle.
 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

 // Use the handle to suspend the created task.
 vTaskSuspend( xHandle );

 // The created task will not run during this period, unless
 // another task calls vTaskResume( xHandle ).

 // Resume the suspended task ourselves.
 vTaskResume( xHandle );

 // The created task will once again get microcontroller processing
 // time in accordance with its priority within the system.
}

vTaskSuspendAll,xTaskResumeAll
Suspends the scheduler without disabling interrupts.
Context switches will not occur while the scheduler is suspended.
After calling vTaskSuspendAll () the calling task will continue to execute without risk of being swapped out until a call to xTaskResumeAll () has been made.
API functions that have the potential to cause a context switch (for example, vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler is suspended.

void vTask1( void * pvParameters )
{
 for( ;; )
 {
     // Task code goes here.

     // ...

     // At some point the task wants to perform a long operation during
     // which it does not want to get swapped out.  It cannot use
     // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
     // operation may cause interrupts to be missed - including the
     // ticks.

     // Prevent the real time kernel swapping out the task.
     vTaskSuspendAll ();

     // Perform the operation here.  There is no need to use critical
     // sections as we have all the microcontroller processing time.
     // During this time interrupts will still operate and the kernel
     // tick count will be maintained.

     // ...

     // The operation is complete.  Restart the kernel.
     xTaskResumeAll ();
 }
}

xTaskGetTickCount
The count of ticks since vTaskStartScheduler was called
uxTaskGetNumberOfTasks
The number of tasks that the real time kernel is currently managing. This includes all ready, blocked and suspended tasks. A task that has been deleted but not yet freed by the idle task will also be included in the count.
xTaskGetIdleTaskHandle,xTaskGetIdleTaskHandleForCPU
It is not valid to call xTaskGetIdleTaskHandle() before the scheduler has been started.

The Idle Task
The idle task is created automatically when the RTOS scheduler is started to ensure there is always at least one task that is able to run. It is created at the lowest possible priority to ensure it does not use any CPU time if there are higher priority application tasks in the ready state.
The idle task is responsible for freeing memory allocated by the RTOS to tasks that have since been deleted. It is therefore important in applications that make use of the vTaskDelete() function to ensure the idle task is not starved of processing time. The idle task has no other active functions so can legitimately be starved of microcontroller time under all other conditions.
It is possible for application tasks to share the idle task priority (tskIDLE_PRIORITY). See the configIDLE_SHOULD_YIELD configuration parameter for information on how this behaviour can be configured.

RTOS (Real Time Operating System)

What is Real Time Operating System (RTOS)- How it works?

                When we hear the word “Operating System” the first ones that come to our mind are those we experience/use in our day to day life, say, Windows XP, Linux, Ubuntu, Windows 7 for Computer systems, Android for mobiles and many more . We mainly know that operating systems are for computers. It is a fact that most of the digital electronic devices run some sort of operating systems inside. There are many operating systems developed for micro controllers too. But here it is familiar as REAL TIME OPERATING SYSTEM. The phrase ‘REAL TIME’ indicates that the response of the operating systems is quick. Microcontrollers don’t have much space for code. Thus the operating systems have less scope to be advanced. They try to provide at least the minimum scope of threading, scheduling and monitoring of multiple tasks for small systems.

                Usually, Real Time Operating Systems are a segment or a part of the whole program that decides the next task, task priority, handles the task messages and coordinates all of the tasks. An RTOS is a complex concept. I’d like to discuss about the concept of State Machine. Here is an implementation of what you can merrily call a state machine.

while(1)
switch(state)
{ case 1: //Code for Task 1;
state= 2;
case 2: //Code for Task 2;
state= 3;
case 3: //Code for Task 3;
state= 4;
case 4: //Code for Task 4;
state=1;
}

As you can see from the code, there is a provision of changing the execution sequence. And it can be further modified and made complex. The programmer can modify and place decision making statements (Like if, if-else, switch-case) to switch the task. And the flow of execution can be logically determined.

                A Real time operating system handles some tasks or routines to be run. The kernel of the operating system assigns CPU attention to a particular task for a period of time. It also checks the task priority, arranges the massages from tasks and schedules.

The basic functionalities an RTOS are:

  • Scheduler
  • RTOS Services
  • Synchronization and messaging

The Scheduler

Tasks, can have three states.

  • Ready to run:  When task have all the resources to run, but not in running state. It’s called a ready to run task. It’s the state before running.
  • Running  – When a task is executing. It is known as running.
  • Blocked – When a task doesn’t have enough resources to run, it is sent to a blocked state.

        To schedule a task, three techniques are adapted.

  • Co-operative scheduling:  In this scheme, a task runs, until it completes its execution.
  • Round Robin Scheduling: Each task is assigned a fixed time slot in this scheme. The task needs to complete its execution. Otherwise, the task may lose its flow, and data generated or it would have to wait for its next turn.
  • Preemptive Scheduling:  This scheduling scheme includes priority dependent time allocation. Usually in programs, 256 priority level is generally used. Each task is assigned an unique priority level. While some system may support more priority levels, and multiple tasks may have same priorities.

The Kernel takes care of the task. It involves the following

  • Creating a task
  • Deleting a task
  • Changing the priority of the task
  • Changing state of the task

RTOS Services

The heart of every operating system is called ‘kernel’. Tasks are relived of monitoring the hardware. It’s the responsibility of the kernel to manage and allocate the resources. As tasks cannot acquire CPU attention all the time, the kernel must also provide some more services. These includes,

  • Interrupt handling services
  • Time services
  • Device management services
  • Memory management services
  • Input-output services

Messaging

Messaging provides a means of communication with other system and between the tasks. The messaging services includes

  • Semaphores
  • Event flags
  • Mailboxes
  • Pipes
  • Message queues

Semaphores are used to synchronize access to shared resources, such as common data areas. Event flags are used to synchronize the inter-task activities. Mailboxes, pipes, and message queues are used to send messages among tasks.

Arduino AND ESP32 Wire Library

functions

begin()
Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once.
Parameters
Arduino
address: the 7-bit slave address (optional); if not specified, join the bus as a master.
ESP32
there are no address only you can select scl gpio , sda gpio and frequency

requestFrom()
Used by the master to request bytes from a slave device.
Syntax
Wire.requestFrom(address, quantity)
Wire.requestFrom(address, quantity, stop)
Parameters
address: the 7-bit address of the device to request bytes from
quantity: the number of bytes to request
stop : boolean. true will send a stop message after the request, releasing the bus. false will continually send a restart after the request, keeping the connection active.
Returns
byte : the number of bytes returned from the slave device

beginTransmission()
Begin a transmission to the I2C slave device with the given address.
Parameters :
the 7-bit address of the device to transmit to

endTransmission()
Ends a transmission to a slave device
Syntax
Wire.endTransmission()
Wire.endTransmission(stop)
Parameters
stop : boolean. true will send a stop message, releasing the bus after transmission. false will send a restart, keeping the connection active.
Returns
byte, which indicates the status of the transmission:

  • 0:success
  • 1:data too long to fit in transmit buffer
  • 2:received NACK on transmit of address
  • 3:received NACK on transmit of data
  • 4:other error

write()
Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a master to slave device (in-between calls to beginTransmission() and endTransmission()).
Syntax
Wire.write(value)
Wire.write(string)
Wire.write(data, length)
Parameters
value: a value to send as a single byte
string: a string to send as a series of bytes
data: an array of data to send as bytes
length: the number of bytes to transmit
Returns
byte: write() will return the number of bytes written, though reading that number is optional

available()
Returns the number of bytes available for retrieval with read(). This should be called on a master device after a call to requestFrom() or on a slave inside the onReceive() handler.
Returns
The number of bytes available for reading.

read()
Reads a byte that was transmitted from a slave device to a master after a call to requestFrom() or was transmitted from a master to a slave.
Returns
The next byte received

SetClock()
This function modifies the clock frequency for I2C communication. I2C slave devices have no minimum working clock frequency, however 100KHz is usually the baseline.
Syntax
Wire.setClock(clockFrequency)
Parameters
clockFrequency: the value (in Hertz) of desired communication clock. Accepted values are 100000 (standard mode) and 400000 (fast mode). Some processors also support 10000 (low speed mode), 1000000 (fast mode plus) and 3400000 (high speed mode). Please refer to the specific processor documentation to make sure the desired mode is supported.

onReceive()
Registers a function to be called when a slave device receives a transmission from a master.
Parameters
handler: the function to be called when the slave receives data; this should take a single int parameter (the number of bytes read from the master) and return nothing, e.g.: void myHandler(int numBytes)

onRequest()
Register a function to be called when a master requests data from this slave device.
Parameters
handler: the function to be called, takes no parameters and returns nothing

ESP32

in esp32 you can create more than one i2c bus by Create Object from TwoWire instead of using public general object Wire

The ESP32 supports I2C communication through its two I2C bus interfaces that can serve as I2C master or slave, depending on the user’s configuration. Accordingly to the ESP32 datasheet, the I2C interfaces of the ESP32 supports:

  • Standard mode (100 Kbit/s) 
  • Fast mode (400 Kbit/s) 
  • Up to 5 MHz, yet constrained by SDA pull-up strength 
  • 7-bit/10-bit addressing mode 
  • Dual addressing mode. Users can program command registers to control I²C interfaces, so that they have more flexibility

The SDA and SCL lines are active low, so they should be pulled up with resistors. Typical values are 4.7k Ohm for 5V devices and 2.4k Ohm for 3.3V devices.

Most sensors we use in our projects are breakout boards that already have the resistors built-in. So, usually, when you’re dealing with this type of electronics components you don’t need to worry about this.

Connecting an I2C device to an ESP32 is normally as simple as connecting GND to GND, SDA to SDA, SCL to SCL and a positive power supply to a peripheral, usually 3.3V (but it depends on the module you’re using).

Examples

master esp32

#include <Arduino.h>
#include <Wire.h>


TwoWire i2c(0);//you can use general Wire object or create one by TwoWire

void setup(){
  Serial.begin(9600);
  i2c.begin(33,32,8000000);
}

void loop(){
  i2c.beginTransmission(4);
  i2c.write("hello there");
  Serial.println("send data");
  i2c.endTransmission();
  delay(1000);
  i2c.requestFrom(4,10,true);// true for stop connection for can use beginTransmission next time
  while(i2c.available())    // slave may send less than requested
  { 
    char c = i2c.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }
  delay(5000);

}

slave arduino

#include<Wire.h>

void receiveEvent(int coutn){
  while(0 < Wire.available()) 
  {
    char c = Wire.read(); 
    Serial.print(c);         
  }
  Serial.println("");
}

void requestEvent(int count){
  Wire.write("hi how are");
  Serial.println(String("master request ")+String(count));
}

void setup() {
  Serial.begin(9600);
  Wire.begin(4);
  
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

}

void loop() {
  delay(100);

}

Scan available addresses

#include <Wire.h>
 
void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}
 
void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
  delay(5000);          
}
Tagged : / /

containers and Iterators in C++

Iterators

class MyContainer{
public:
    const int* array;
    int size;
    class Iterator{
        const int *obj;
    public:
        Iterator(const int* obj):obj(obj){
        }

        bool operator !=(Iterator other){
            cout<<"call != \n";
            if(other.obj==this->obj)
                return false;
            return true;
        }

        Iterator operator++(){
            obj++;
            cout<<"call ++ \n";
            return *this;
        }

        const int& operator*()const {
            cout<<"call * \n";
            return *obj;
        }

    };
    template <int i>
    MyContainer(int const (&obj)[i]):array(obj),size(i){

    }

    Iterator begin(){
        MyContainer::Iterator iterator(this->array);
        cout<<"call begin\n";
        return iterator;
    }

    Iterator end() {
        MyContainer::Iterator iterator(this->array+size);
        cout << "call begin\n";
        return iterator;

    }
};

int main(int argc,char* argv[]){
    MyContainer mc({1,2,3,4,5,6});
    for(const auto& vv:mc){
        cout<<vv<<endl;
    }

    return 0;
}

simple forward iterator and the result is

call begin
call !=
call *
1
call ++
call !=
call *
2
call ++
call !=
call *
3
call ++
call !=
call *
4
call ++
call !=
call *
5
call ++
call !=
call *
6
call ++
call !=

Iterator categoryPropertiesContainer
Forward iterator++It, It++, *Itunordered associative container
 It == It2, It != It2std::forward_list
Bidirectional iterator--It, It--ordered associative container
  std::list
Random access iteratorIt[i]std::array
 It+= n, It-= nstd::vector
 It+n, It-nstd::deque
 n+Itstd::string
 It-It2 
 It < It2, It <= It2, It > It2 
 It >= It2 
Global functionDescription
std::begin(cont)Returns a begin iterator to the container cont.
std::end(cont)Returns an end iterator to the container cont.
std::rbegin(cont)Returns a reverse begin iterator to the container cont.
std::rend(cont)Returns a reverse end iterator to the container cont.
std::cbegin(cont)Returns a constant begin iterator to the container cont.
std::cend(cont)Returns a constant end iterator to the container cont.
std::crbegin(cont)Returns a reverse constant begin iterator to the container cont.
std::crend(cont)Returns a reverse constant end iterator to the container cont.
std::prev(it)Returns an iterator, which points to a position before it
std::next(it)Returns an iterator, which points to a position after it.
std::distance(fir, sec)Returns the number of elements between fir and sec.
std::advance(it, n)Puts the iterator it n positions further.
general functions
// iteratorUtilities.cpp
...
#include <iterator>
...
using std::cout;

std::unordered_map<std::string, int> myMap{{"Rainer", 1966}, {"Beatrix", 1966},
                                           {"Juliette", 1997}, {"Marius", 1999}};

for (auto m: myMap) cout << "{" << m.first << "," << m.second << "} ";
     // {Juliette,1997},{Marius,1999},{Beatrix,1966},{Rainer,1966}

auto mapItBegin= std::begin(myMap);
cout << mapItBegin->first << " " << mapItBegin->second; // Juliette 1997

auto mapIt= std::next(mapItBegin);
cout << mapIt->first << " " << mapIt->second;            // Marius 1999
cout << std::distance(mapItBegin, mapIt);                 // 1

std::array<int, 10> myArr{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto a: myArr) std::cout << a << " ";               // 0 1 2 3 4 5 6 7 8 9

auto arrItEnd= std::end(myArr);
auto arrIt= std::prev(arrItEnd);
cout << *arrIt << std::endl;                             // 9

std::advance(arrIt, -5);
cout << *arrIt;                                          // 4

Containers

The table shows you the constructors and destructors of a container. A std:vector stands for the rest of them.

TypeExample
Defaultstd::vector<int> vec1
Rangestd::vector<int> vec2(vec1.begin(), vec1.end())
Copystd::vector<int> vec3(vec2)
Copystd::vector<int> vec3= vec2
Movestd::vector<int> vec4(std::move(vec3))
Movestd::vector<int> vec4= std::move(vec3)
Sequence (Initializer list)std::vector<int> vec5 {1, 2, 3, 4, 5}
Sequence (Initializer list)std::vector<int> vec5= {1, 2, 3, 4, 5}
  
Destructorvec5.~vector()
Delete elementsvec5.clear()

Because std::array is generated at compile time, there are a few things that are special. std::array has no move constructor and can neither be created with a range nor with an initialiser list. However, you can initialize a std::array with an aggregate initialisation. Also, std::array has no method for removing its elements.

#include <map>
#include <unordered_map>
#include <vector>
...
using namespace std;

vector<int> vec= {1, 2, 3, 4, 5, 6, 7, 8, 9};
map<string, int> m= {{"bart", 12345}, {"jenne", 34929}, {"huber", 840284} };
unordered_map<string, int> um{m.begin(), m.end()};

for (auto v: vec) cout << v << " "; // 1 2 3 4 5 6 7 8 9
for (auto p: m) cout << p.first << "," << p.second << " ";
                                    // bart,12345 huber,840284 jenne,34929
for (auto p: um) cout << p.first << "," << p.second << " ";
                                    // bart,12345 jenne,34929 huber,840284

vector<int> vec2= vec;
cout << vec.size() << endl; // 9
cout << vec2.size() << endl; // 9

vector<int> vec3= move(vec);
cout << vec.size() << endl; // 0
cout << vec3.size() << endl; // 9

vec3.clear();
cout << vec3.size() << endl; // 0

size+empty+max_size

#include <map>
#include <set>
#include <vector>
...
using namespace std;

vector<int> intVec{1, 2, 3, 4, 5, 6, 7, 8, 9};
map<string, int> str2Int = {{"bart", 12345}, 
                            {"jenne", 34929}, {"huber", 840284}};
set<double> douSet{3.14, 2.5};

cout << intVec.empty() << endl;  // false
cout << str2Int.empty() << endl; // false
cout << douSet.empty() << endl;  // false

cout << intVec.size() << endl;  // 9
cout << str2Int.size() << endl; // 3
cout << douSet.size() << endl;  // 2

cout << intVec.max_size() << endl;  // 4611686018427387903
cout << str2Int.max_size() << endl; // 384307168202282325
cout << douSet.max_size() << endl;  // 461168601842738790

std::copy

template<class InputIterator, class OutputIterator>
  OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
  while (first!=last) {
    *result = *first;
    ++result; ++first;
  }
  return result;
}

access

IteratorDescription
cont.begin() and cont.end()Pair of iterators to iterate forward.
cont.cbegin() and cont.cend()Pair of iterators to iterate const forward.
cont.rbegin() and cont.rend()Pair of iterators to iterate backward.
cont.crbegin() and cont.crend()Pair of iterators to iterate const backward.

assign new elements
You can assign new elements to existing containers or swap two containers. For the assignment of a container cont2 to a container cont, there exists the copy assignment cont= cont2 and the move assignment cont= std::move(cont2). A special form of assignment is the one with an initialiser list: cont= {1, 2, 3, 4, 5}. That’s not possible for std::array, but you can instead use the aggregate initialisation. The function swap exists in two forms. You have it as a method cont(swap(cont2)) or as a function template std::swap(cont, cont2).

comparison operators
Containers support the comparison operators ==!=<><=>=. The comparison of two containers happens on the elements of the containers. If you compare associative containers, their key is compared. Unordered associative containers support only the comparison operator == and !=.

Sequential Container

Criteriaarrayvectordequelistforward_list
Sizestaticdynamicdynamicdynamicdynamic
Implementationstatic arraydynamic arraysequence of arraysdoubled linked listsingle linked list
Accessrandomrandomrandomforward and backwardforward
Optimized for insert and delete at end: O(1)begin and end: O(1)begin and end: O(1)begin(1)
    arbitrary: O(1)arbitrary: O(1)
Memory reservation yesnonono
Release of memory shrink_to_fitshrink_to_fitalwaysalways
Strengthno memory allocation; minimal memory requirements95% solutioninsertion and deletion at the begin and endinsertion and deletion at an arbitrary positionfast insertion and deletion; minimal memory requirements
Weaknessno dynamic memoryInsertion and deletionInsertion and deletionno random accessno random access
 memory allocationat an arbitrary position: O(n)at an arbitrary position: O(n)  
vector

std::vector is a homogeneous container, for which it’s length can be adjusted at runtime. std::vector needs the header <vector>. As it stores its elements contiguously in memory, std::vector support pointer arithmetic.

deque

std::deque, which consists of a sequence of arrays, is quite similar to std::vectorstd::deque need the header <deque>. The std::deque has three additional methods deq.push_front(elem)deq.pop_front() and `deq.emplace_front(args… ) to add or remove elements at its beginning.

list

std::list is a doubled linked list. std::list needs the header <list>.

Although it has a similar interface to std::vector or std::dequestd::list is quite different to both of them. That’s due to its structure.

Associative Container

Associative containerSortedAssociated valueMore identical keysAccess time
std::setyesnonologarithmic
std::unordered_setnononoconstant
std::mapyesyesnologarithmic
std::unordered_mapnoyesnoconstant
std::multisetyesnoyeslogarithmic
std::unordered_multisetnonoyesconstant
std::multimapyesyesyeslogarithmic
std::unordered_multimapnoyesyesconstant

Adaptors for Containers

stack

...
#include <stack>
...
std::stack<int> myStack;

std::cout << myStack.empty() << std::endl;   // true
std::cout << myStack.size() << std::endl;    // 0

myStack.push(1);
myStack.push(2);
myStack.push(3);
std::cout << myStack.top() << std::endl;     // 3

while (!myStack.empty()){ 
  std::cout << myStack.top() << " ";
  myStack.pop();
}                                            // 3 2 1

std::cout << myStack.empty() << std::endl;   // true
std::cout << myStack.size() << std::endl;    // 0

queue

...
#include <queue>
...
std::queue<int> myQueue;

std::cout << myQueue.empty() << std::endl;    // true
std::cout << myQueue.size() << std::endl;     // 0

myQueue.push(1);
myQueue.push(2);
myQueue.push(3);
std::cout << myQueue.back() << std::endl;     // 3
std::cout << myQueue.front() << std::endl;    // 1

while (!myQueue.empty()){
  std::cout << myQueue.back() << " ";
  std::cout << myQueue.front() << " : ";
  myQueue.pop();
}                                             // 3 1 : 3 2 : 3 3

std::cout << myQueue.empty() << std::endl;    // true
std::cout << myQueue.size() << std::endl;     // 0

priority_queue 

#include <queue>
...
std::priority_queue<int> myPriorityQueue;

std::cout << myPriorityQueue.empty() << std::endl;   // true
std::cout << myPriorityQueue.size() << std::endl;    // 0

myPriorityQueue.push(3);
myPriorityQueue.push(1);
myPriorityQueue.push(2);
std::cout << myPriorityQueue.top() << std::endl;     // 3

while (!myPriorityQueue.empty()){
  std::cout << myPriorityQueue.top() << " ";
  myPriorityQueue.pop();
}                                                    // 3 2 1

std::cout << myPriorityQueue.empty() << std::endl;   // true
std::cout << myPriorityQueue.size() << std::endl;    // 0

std::priority_queue<std::string, std::vector<std::string>,
                    std::greater<std::string>> myPriorityQueue2;

myPriorityQueue2.push("Only");
myPriorityQueue2.push("for");
myPriorityQueue2.push("testing");
myPriorityQueue2.push("purpose");
myPriorityQueue2.push(".");

while (!myPriorityQueue2.empty()){
  std::cout << myPriorityQueue2.top() << " ";
  myPriorityQueue2.pop();
}                                // . Only for purpose testing

back_inserter + front_inserter + inserter

#include <iterator>
...
std::deque<int> deq{5, 6, 7, 10, 11, 12};
std::vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

std::copy(std::find(vec.begin(), vec.end(), 13),
          vec.end(), std::back_inserter(deq));

for (auto d: deq) std::cout << d << " ";
                    // 5 6 7 10 11 12 13 14 15

std::copy(std::find(vec.begin(), vec.end(), 8),
std::find(vec.begin(), vec.end(), 10),
std::inserter(deq,
std::find(deq.begin(), deq.end(), 10)));d
for (auto d: deq) std::cout << d << " ";
                    // 5 6 7 8 9 10 11 12 13 14 15

std::copy(vec.rbegin()+11, vec.rend(),
std::front_inserter(deq));
for (auto d: deq) std::cout << d << " ";
                    // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Utility libraries in c++

initializer_list
template< class T >
class initializer_list;
https://en.cppreference.com/w/cpp/utility/initializer_list

sizereturns the number of elements in the initializer list
(public member function)
beginreturns a pointer to the first element
(public member function)
endreturns a pointer to one past the last element
(public member function)
#include <iostream>
#include <vector>
#include <initializer_list>
 
template <class T>
struct S {
    std::vector<T> v;
    S(std::initializer_list<T> l) : v(l) {
         std::cout << "constructed with a " << l.size() << "-element list\n";
    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }
    std::pair<const T*, std::size_t> c_arr() const {
        return {&v[0], v.size()};  // copy list-initialization in return statement
                                   // this is NOT a use of std::initializer_list
    }
};
 
template <typename T>
void templated_fn(T) {}
 
int main()
{
    S<int> s = {1, 2, 3, 4, 5}; // copy list-initialization
    s.append({6, 7, 8});      // list-initialization in function call
 
    std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";
 
    for (auto n : s.v)
        std::cout << n << ' ';
    std::cout << '\n';
 
    std::cout << "Range-for over brace-init-list: \n";
 
    for (int x : {-1, -2, -3}) // the rule for auto makes this ranged-for work
        std::cout << x << ' ';
    std::cout << '\n';
 
    auto al = {10, 11, 12};   // special rule for auto
 
    std::cout << "The list bound to auto has size() = " << al.size() << '\n';
 
//    templated_fn({1, 2, 3}); // compiler error! "{1, 2, 3}" is not an expression,
                             // it has no type, and so T cannot be deduced
    templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK
    templated_fn<std::vector<int>>({1, 2, 3});           // also OK
}
using namespace std;

template<class T>
class A{
public:
    template <std::size_t N>
    A(const T (&t)[N]){//or &&t
        cout<< t[0];
        cout<<N;
    }
};

int main(int argc,char* argv[]){
    int arr[]={1,2};
    A a({1,2});
    return 0;
}

tuple
pair is like tuple but with two elements first and second
https://en.cppreference.com/w/cpp/utility/tuple

#include <tuple>
#include <iostream>
#include <string>
#include <stdexcept>
 
std::tuple<double, char, std::string> get_student(int id)
{
    if (id == 0) return std::make_tuple(3.8, 'A', "Lisa Simpson");
    if (id == 1) return std::make_tuple(2.9, 'C', "Milhouse Van Houten");
    if (id == 2) return std::make_tuple(1.7, 'D', "Ralph Wiggum");
    throw std::invalid_argument("id");
}
 
int main()
{
    auto student0 = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';
 
    double gpa1;
    char grade1;
    std::string name1;
    std::tie(gpa1, grade1, name1) = get_student(1);
    std::cout << "ID: 1, "
              << "GPA: " << gpa1 << ", "
              << "grade: " << grade1 << ", "
              << "name: " << name1 << '\n';
 
    // C++17 structured binding:
    auto [ gpa2, grade2, name2 ] = get_student(2);
    std::cout << "ID: 2, "
              << "GPA: " << gpa2 << ", "
              << "grade: " << grade2 << ", "
              << "name: " << name2 << '\n';
}
std::tuple<int, int> foo_tuple() 
{
  return {1, -1};  // Error until N4387
  return std::tuple<int, int>{1, -1}; // Always works
  return std::make_tuple(1, -1); // Always works
}

apply

int add(int first, int second) { return first + second; }
 
template<typename T>
T add_generic(T first, T second) { return first + second; }
 
auto add_lambda = [](auto first, auto second) { return first + second; };
 
template<typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple)
{
    std::apply
    (
        [&os](Ts const&... tupleArgs)
        {
            os << '[';
            std::size_t n{0};
            ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
            os << ']';
        }, theTuple
    );
    return os;
}
 
int main()
{
    // OK
    std::cout << std::apply(add, std::pair(1, 2)) << '\n';
 
    // Error: can't deduce the function type
    // std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n'; 
 
    // OK
    std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n'; 
 
    // advanced example
    std::tuple myTuple(25, "Hello", 9.31f, 'c');
    std::cout << myTuple << '\n';
 
}

move

std::move is used to indicate that an object t may be “moved from”, i.e. allowing the efficient transfer of resources from t to another object.
In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.

#include <iostream>
#include <utility>
#include <vector>
#include <string>
 
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
 
    // uses the push_back(const T&) overload, which means 
    // we'll incur the cost of copying str
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n";
 
    // uses the rvalue reference push_back(T&&) overload, 
    // which means no strings will be copied; instead, the contents
    // of str will be moved into the vector.  This is less
    // expensive, but also means str might now be empty.
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";
 
    std::cout << "The contents of the vector are \"" << v[0]
                                         << "\", \"" << v[1] << "\"\n";
}
After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"

forward

template<class T>
void wrapper(T&& arg) 
{
    // arg is always lvalue
    foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T
}
struct Arg
{
    int i = 1;
    int  get() && { return i; } // call to this overload is rvalue
    int& get() &  { return i; } // call to this overload is lvalue
};
#include <iostream>
#include <memory>
#include <utility>
 
struct A {
    A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; }
    A(int& n)  { std::cout << "lvalue overload, n=" << n << "\n"; }
};
 
class B {
public:
    template<class T1, class T2, class T3>
    B(T1&& t1, T2&& t2, T3&& t3) :
        a1_{std::forward<T1>(t1)},
        a2_{std::forward<T2>(t2)},
        a3_{std::forward<T3>(t3)}
    {
    }
 
private:
    A a1_, a2_, a3_;
};
 
template<class T, class U>
std::unique_ptr<T> make_unique1(U&& u)
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)));
}
 
template<class T, class... U>
std::unique_ptr<T> make_unique2(U&&... u)
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}
 
int main()
{   
    auto p1 = make_unique1<A>(2); // rvalue
    int i = 1;
    auto p2 = make_unique1<A>(i); // lvalue
 
    std::cout << "B\n";
    auto t = make_unique2<B>(2, i, 3);
}
rvalue overload, n=2
lvalue overload, n=1
B
rvalue overload, n=2
lvalue overload, n=1
rvalue overload, n=3

exchange

template<class T, class U = T>
constexpr // since C++20
T exchange(T& obj, U&& new_value)
{
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;
}

swap

int main()
{
   int a = 5, b = 3;
 
   // before
   std::cout << a << ' ' << b << '\n';
 
   std::swap(a,b);
 
   // after
   std::cout << a << ' ' << b << '\n';
}

bitset
set,reset,flip,size,all,any,none ~^|&
https://en.cppreference.com/w/cpp/utility/bitset

#include <bitset>
 
int main()
{
    std::bitset<8> b1(42);
    for (std::size_t i = 0; i < b1.size(); ++i) {
        std::cout << "b1[" << i << "]: " << b1[i] << '\n';
    }
    b1[0] = true; // modifies the first bit through bitset::reference
    std::cout << "After setting bit 0, the bitset holds " << b1 << '\n';
}

type_traits
https://en.cppreference.com/w/cpp/header/type_traits

Composite type categoriesPrimary type category
is_arithmeticis_floating_point or is_integral
is_fundamentalis_arithmetic or is_void
is_objectis_arithmetic or is_enum or is_pointer or is_member_pointer
is_referenceis_lvalue_reference or is_rvalue_reference
is_compoundcomplement of is_fundamental
is_member_pointeris_member_object_pointer or is_member_function_pointer
template class Base, class Derived>Checks if Derived is derived from Base.
struct is_base_of 
template <class From, class To>Checks if From can be converted to To.
struct is_convertible 
template <class T, class U>Checks if the types T and U are the same.
struct is_same
#include <type_traits>
using std::cout;

cout << std::is_void<void>::value;             // true
cout << std::is_integral<short>::value;        // true
cout << std::is_floating_point<double>::value; // true
cout << std::is_array<int [] >::value;         // true
cout << std::is_pointer<int*>::value;          // true
cout << std::is_reference<int&>::value;        // true

struct A{
  int a;
  int f(int){ return 2011; }
};
cout << std::is_member_object_pointer<int A::*>::value;          // true
cout << std::is_member_function_pointer<int (A::*)(int)>::value; // true

enum E{
  e= 1,
};
cout << std::is_enum<E>::value;                  // true

union U{
  int u;
};
cout << std::is_union<U>::value;                 // true

cout << std::is_class<std::string>::value;       // true
cout << std::is_function<int * (double)>::value; // true
cout << std::is_lvalue_reference<int&>::value;   // true
cout << std::is_rvalue_reference<int&&>::value;  // true
cout << is_const<int>::value;                     // false
cout << is_const<const int>::value;               // true
cout << is_const<add_const<int>::type>::value;    // true

typedef add_const<int>::type myConstInt;
cout << is_const<myConstInt>::value;              //true

typedef const int myConstInt2;
cout << is_same<myConstInt, myConstInt2>::value;  // true

cout << is_same<int, remove_const<add_const<int>::type>::type>::value;    // true
cout << is_same<const int, add_const<add_const<int>::type>::type>::value; // true

functional
https://en.cppreference.com/w/cpp/header/functional

...
#include <functional>
...
// for placehoder _1 and _2
using namespace std::placeholders; f

using std::bind;
using std::function
...
double divMe(double a, double b){ return a/b; };
function < double(double, double) > myDiv1= bind(divMe, _1, _2);
function < double(double) > myDiv2= bind(divMe, 2000, _1);

divMe(2000, 10) == myDiv1(2000, 10) == myDiv2(10);
#include <functional>
...
using std::make_pair;
using std::map;

map<const char, std::function<double(double, double)>> tab;
tab.insert(make_pair('+', [](double a, double b){ return a + b; }));
tab.insert(make_pair('-', [](double a, double b){ return a - b; }));
tab.insert(make_pair('*', [](double a, double b){ return a * b; }));
tab.insert(make_pair('/', [](double a, double b){ return a / b; }));

std::cout << tab['+'](3.5, 4.5);  // 8
std::cout << tab['-'](3.5, 4.5);  // -1
std::cout << tab['*'](3.5, 4.5);  // 15.75
std::cout << tab['/'](3.5, 4.5);  // 0.777778
Function typeReturn typeType of the arguments
double(double, double)doubledouble
int()int 
double(int, double)doubleint, double
void()  

std::variant

std::variant is a type-safe union. An instance of std::variant has a value from one of its types. The type must not be a reference, array or void

#include <variant>

...

std::variant<int, float> v, w;
v = 12;                             // v contains int
int i = std::get<int>(v);
w = std::get<int>(v);
w = std::get<0>(v);                 // same effect as the previous line
w = v;                              // same effect as the previous line
 
//  std::get<double>(v);            // error: no double in [int, float]
//  std::get<3>(v);                 // error: valid index values are 0 and 1
 
try{
  std::get<float>(w);               // w contains int, not float: will throw
}
catch (std::bad_variant_access&) {}
 
std::variant<std::string> v("abc"); // converting constructor must be unambiguous
v = "def";                          // converting assignment must be unambiguous

optional
can have value or empty

#include <optional>

std::optional<int> getFirst(const std::vector<int>& vec){
  if (!vec.empty()) return std::optional<int>(vec[0]);
  else return std::optional<int>();
}

...
    
std::vector<int> myVec{1, 2, 3};
std::vector<int> myEmptyVec;
    
auto myInt= getFirst(myVec);
    
if (myInt){
  std::cout << *myInt << std::endl;                       // 1
  std::cout << myInt.value() << std::endl;                // 1
  std::cout << myInt.value_or(2017) << std::endl;         // 1
}
    
auto myEmptyInt= getFirst(myEmptyVec);
    
if (!myEmptyInt){
  std::cout << myEmptyInt.value_or(2017) << std::endl;   // 2017
}  

std::any

std::any is a type-safe container for single values of any type which is copy-constructible. 

#include <algorithm>
#include <any>
#include <iostream>
#include <string>
#include <vector>
 
class Star
{
    std::string name;
    int id;
 
public:
    Star(std::string name, int id) : name { name }, id { id }
    {
        std::cout << "Star::Star(string, int)\n";
    }
 
    void print() const
    {
        std::cout << "Star{ \"" << name << "\" : " << id << " };\n";
    }
};
 
auto main() -> int
{
    std::any celestial;
    // (1) emplace( Args&&... args );
    celestial.emplace<Star>("Procyon", 2943);
    const auto* star = std::any_cast<Star>(&celestial);
    star->print();
 
    std::any av;
    // (2) emplace( std::initializer_list<U> il, Args&&... args );
    av.emplace<std::vector<char>>({ 'C', '+', '+', '1', '7' } /* no args */ );
    std::cout << av.type().name() << '\n';
    const auto* va = std::any_cast<std::vector<char>>(&av);
    std::for_each(va->cbegin(), va->cend(), [](char const& c) { std::cout << c; });
    std::cout << '\n';
}
#include <any>

struct MyClass{};

...

std::vector<std::any> anyVec{true, 2017, std::string("test"), 3.14,  MyClass()};
std::cout << std::any_cast<bool>(anyVec[0]);                          // true
int myInt= std::any_cast<int>(anyVec[1]);                                        
std::cout << myInt << std::endl;                                      // 2017
    
std::cout << anyVec[0].type().name();                                 // b 
std::cout << anyVec[1].type().name();                                 // i
any a=any(12);
a.emplace<B>(B());
cout<<a.type().name();//1B

Conversion in c++

static_cast
Converts between types using a combination of implicit and user-defined conversions

#include <vector>
#include <iostream>
 
struct B {
    int m = 0;
    void hello() const {
        std::cout << "Hello world, this is B!\n";
    }
};
struct D : B {
    void hello() const {
        std::cout << "Hello world, this is D!\n";
    }
};
 
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
 
int main()
{
    // 1: initializing conversion
    int n = static_cast<int>(3.14); 
    std::cout << "n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "v.size() = " << v.size() << '\n';
 
    // 2: static downcast
    D d;
    B& br = d; // upcast via implicit conversion
    br.hello();
    D& another_d = static_cast<D&>(br); // downcast
    another_d.hello();
 
    // 3: lvalue to xvalue
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
    std::cout << "after move, v.size() = " << v.size() << '\n';
 
    // 4: discarded-value expression
    static_cast<void>(v2.size());
 
    // 5. inverse of implicit conversion
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "*ni = " << *ni << '\n';
 
    // 6. array-to-pointer followed by upcast
    D a[10];
    B* dp = static_cast<B*>(a);
 
    // 7. scoped enum to int or float
    E e = E::ONE;
    int one = static_cast<int>(e);
    std::cout << one << '\n';
 
    // 8. int to enum, enum to another enum
    E e2 = static_cast<E>(one);
    EU eu = static_cast<EU>(e2);
 
    // 9. pointer to member upcast
    int D::*pm = &D::m;
    std::cout << br.*static_cast<int B::*>(pm) << '\n';
 
    // 10. void* to any type
    void* voidp = &e;
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

dynamic_cast
Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy

struct V {
    virtual void f() {};  // must be polymorphic to use runtime-checked dynamic_cast
};
struct A : virtual V {};
struct B : virtual V {
  B(V* v, A* a) {
    // casts during construction (see the call in the constructor of D below)
    dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
    dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
  }
};
struct D : A, B {
    D() : B(static_cast<A*>(this), this) { }
};
 
struct Base {
    virtual ~Base() {}
};
 
struct Derived: Base {
    virtual void name() {}
};
 
int main()
{
    D d; // the most derived object
    A& a = d; // upcast, dynamic_cast may be used, but unnecessary
    D& new_d = dynamic_cast<D&>(a); // downcast
    B& new_b = dynamic_cast<B&>(a); // sidecast
 
 
    Base* b1 = new Base;
    if(Derived* d = dynamic_cast<Derived*>(b1))
    {
        std::cout << "downcast from b1 to d successful\n";
        d->name(); // safe to call
    }
 
    Base* b2 = new Derived;
    if(Derived* d = dynamic_cast<Derived*>(b2))
    {
        std::cout << "downcast from b2 to d successful\n";
        d->name(); // safe to call
    }
 
    delete b1;
    delete b2;
}

const_cast
Converts between types with different cv-qualification.

struct type {
    int i;
 
    type(): i(3) {}
 
    void f(int v) const {
        // this->i = v;                 // compile error: this is a pointer to const
        const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
    }
};
 
int main() 
{
    int i = 3;                 // i is not declared const
    const int& rci = i; 
    const_cast<int&>(rci) = 4; // OK: modifies i
    std::cout << "i = " << i << '\n';
 
    type t; // if this was const type t, then t.f(4) would be undefined behavior
    t.f(4);
    std::cout << "type::i = " << t.i << '\n';
 
    const int j = 3; // j is declared const
    int* pj = const_cast<int*>(&j);
    // *pj = 4;      // undefined behavior
 
    void (type::* pmf)(int) const = &type::f; // pointer to member function
    // const_cast<void(type::*)(int)>(pmf);   // compile error: const_cast does
                                              // not work on function pointers
}

user-defined conversion
Enables implicit conversion or explicit conversion from a class type to another type.

struct X {
    //implicit conversion
    operator int() const { return 7; }
 
    // explicit conversion
    explicit operator int*() const { return nullptr; }
 
//   Error: array operator not allowed in conversion-type-id
//   operator int(*)[3]() const { return nullptr; }
    using arr_t = int[3];
    operator arr_t*() const { return nullptr; } // OK if done through typedef
//  operator arr_t () const; // Error: conversion to array not allowed in any case
};
class A{
public:
   explicit A(int i){
            
   }
};
 
int main()
{
    X x;
 
    int n = static_cast<int>(x);   // OK: sets n to 7
    int m = x;                     // OK: sets m to 7
 
    int* p = static_cast<int*>(x);  // OK: sets p to null
//  int* q = x; // Error: no implicit conversion
 
    int (*pa)[3] = x;  // OK
    A a=(A)12;
}