C++
Introduction and Significance of C++ Thread Class
Multithreading is a fundamental feature in modern computing that allows multiple tasks to execute concurrently, leading to improved performance and responsiveness. The C++ Standard Library provides the std::thread class, which enables developers to create and manage threads efficiently. This class abstracts low-level threading details and offers a convenient API for concurrent programming.
The significance of the C++ std::thread class lies in its ability to enhance application performance by leveraging multi-core processors, reducing execution time for computationally intensive tasks. It is commonly used in applications requiring parallel execution, such as real-time systems, game development, and large-scale data processing. Furthermore, std::thread integrates seamlessly with other C++ concurrency features, such as mutexes, condition variables, and futures, making it an essential tool for writing robust and scalable multithreaded applications.
C++ thread class – handling overloaded functions and return result:
C++ multi-threading library borrowed from boost library and standardized into C++11 standards is a platform neutral threading library framework, an advantage over platform specific or the native API’s. Thus, any multi-threading application developed using this thread class library from the modern C++11 standards certainly lead to cost and time benefits from any team or organization.
The C++11 multithreading library though has certain limitations viz…
- Creation of a thread task with overloaded functions as a unit of work
- Handling of return result or value of a function spawned as a thread
- Providing thread class instance its scheduling priorities and so on…
In this article we shall discuss how to address the first two issues, I shall keep the third issue for a subsequent discussion in a later article.
Scenario-1
Handling of overloaded functions as thread’s unit of work in usual style:
Let’s say we want to spawn the following overloaded global functions as threads with the usual approach, as to how we create thread’s and determine its unit of work for functions that is not overloaded.
void fun(){
cout <<“fun() ” <<this_thread::get_id() << endl;}
void fun(int x){
cout <<“fun(int x) ” <<this_thread::get_id() << endl;}
void fun(float x)
{
cout <<“fun(float x)” <<this_thread::get_id()<< endl;
cout << x*x;
}
//consumer code
thread th1(&fun); //Error
thread th2(&fun, 100); //Error
thread th3(&fun, 45.12f); //Error
//…
th1.join();
th2.join();
th3.join();
So, does that mean the overloaded functions cannot be thread parameters or unit of work?
It is indeed possible, by using an alternate route or approach as stated below:
- Wrap the address in a function pointer variable and then provide the function pointer variable as the thread class instance parameter.
- Wrap these overloaded function calls in a distinct or separate lambda body. Then provide the lambda handle as a thread class instance parameter.
Solution-1:
The function pointer approach:
//consumer code
void(*fp1)(void) = &fun;
void(*fp2)(int) = &fun;
float(*fp3)(float) = &fun;
thread th1(fp1);
th1.join();
thread th2(fp2,100);
th2.join();
thread th3(fp3, 45.12f);
th3.join();
Solution-2:
Wrapping the call inside of a lambda:
auto lm1 = [](){fun();};
auto lm2 = [](int x){fun(x);};
auto lm3 = [](float x){fun(x);};
thread th1(lm1);
th1.join();
thread th2(lm2,100);
th2.join();
thread th3(lm3, 45.12f);
th3.join();
Similarly, if the class has any overloaded members other than functors, then we have to use the same approach as above, wrap each overloaded member function call in a lambda and then provide the lambda handle as thread instance parameter.
Addressing Scenario-2
Now, coming to the issue of handling the return result of a thread function. A thread class primitive by default is not capable of helping us hold the return result of the function if any. There are 2 different ways of handling this return value issue…
- Using std::packaged_task class library (or)
- Using the task-based concurrency function ‘async.’
Since this is an article purely on thread class primitive and not about ‘async’ function, we discuss the utility value of the std::packaged_task class library for the same.
We use the following sequence of operations in the form of code to achieve the same. We shall also look at a suitable code example following the steps mentioned below:
- Create an std::packaged_task class instance and initialize it with the address of the function that is returning a value.
- Define and initialize a std::future class instance with the return result of get_future() member function call w.r.to the std::packaged_task instance we created at step-1.
- Now provide the std::packaged_task class object as the thread class instance parameter. Since the std::packaged_task instance is a move-only type, we have to std::move this instance to the thread class construction.
4) After thread instance join call, we call the get() member function of the std::future class object w.r.to the associated std::packaged_task class object, which would help us retrieve the return result of the function from the shared memory.
The following code example illustrates the same.
int fun1(){
int a=10;
return a*a;
}
float fun2(int x){
return x*3.14f;
}
//consumer code
std::packaged_task<int()> p_task1;
std::future<int> result1 = p_task1.get_future();
std::packaged_task<float(int)> p_task2;
std::future<float> result2 = p_task2.get_future();
thread th1(std::move(p_task1));
th1.join();
thread th2(std::move(p_task2), 10);
th2.join();
int val1{};
float val2{};
if(result1.valid())
val1 = result1.get();
if(result2.valid())
val2 = result2.get();
cout << val1 << endl;
cout << val2 << endl;
The following diagram depicts the shared_memory sharing arrangement between the packaged_task instance and the future object.

Hope, the above discussion was helpful in solving the stated return value issues of functions with respect to the thread class library.
Conclusion
The C++ std::thread class is a powerful and versatile tool that simplifies multithreading, enabling developers to write concurrent programs with ease. Its ability to handle parallel execution efficiently makes it invaluable in modern application development. By leveraging std::thread alongside other concurrency mechanisms, developers can achieve higher performance, better resource utilization, and improved responsiveness in their applications. Understanding and utilizing the features of the C++ std::thread class is essential for creating efficient, reliable, and scalable software solutions.
Default constructor assumption scenarios in C++:
In general, most documentations state or claim that there is a default constructor assumed by the compiler for a class in-case we do not happen to provide one, and it stops there.
With this claim there is a general belief by the C++ community that this is true for every given class in the code for which they do not provide any constructors at all.
This is a wrong assumption, and the claim by most C++ documentations is factually in-correct.
Let us dive in to prove this point. A default constructor assumption by the compiler for a class not defined with any constructor by the programmer is only in the following context’s:
Around 4 different scenarios in Traditional C++ [98-2003] standards and one specific scenario in modern C++.
So, what are the 4 + 1 scenarios where a compiler would assume a default constructor in C++?
Scenario-1: Consider the following example code.

If the base class is provided with our own default constructor and the derived class has not been defined with a default constructor, then the compiler would assume for the derived.
Note: If in-case the base class does not have a default constructor defined, then the compiler would not assume a default constructor for the base class nor for the derived class.

By generating the assembly listing of the above example, we can analyse the same.
Try commenting out the base class constructor definition and re-generate the assembly listing, you will never find the decorated name entries of the constructors.
Scenario-2:
If a container class contains an object of a class having a default constructor defined, then the compiler would assume for the container class as well.
Note: If in-case the contained object belongs to class not having a default constructor, then the compiler would not assume any default constructor for the contained class nor the container class.
Consider the following example code:

Assembly listing of the above example:

Scenario-3:
In the case of virtual (hybrid) inheritance model.
Given the following example, the classes ‘CB’ and ‘CC’ virtual inherit ‘CA’ base class, if we have not defined any constructors for these classes, the compiler would start assuming a default constructor for the derived ‘CB’, ‘CC’ and further down for other derived classes in the inheritance hierarchy.
Consider the following example:


The assembly listing of the above example illustrates the compiler assumed default constructors for the all the derived classes.
Scenario-4:
If a class is polymorphic, that is the class comprises of one or more virtual member functions, and for such a class no default constructor has been provided, then the compiler would assume one for the said class.
The compiler generated default constructor would hold instructions induced by the compiler to initialize the ‘vfptr’ element (an invisible data member of the class – assumed by the compiler) to point to the VFTABLE (virtual function table).

The assembly listing of the example illustrates or proves the same.

Scenario-5:
If we go in for in-class initialization, A C++11 class feature. When we do not happen to provide any constructors for a class using this feature, then the compiler would assume a default constructor.

The Assembly listing proves the point, the compiler has indeed assumed a default constructor.

Lock free Singleton class:
By the definition of Gang of four design pattern goes…
The singleton pattern is a design pattern that is used to ensure that a class can only have one concurrent instance. Whenever additional objects of a singleton class are required, the previously created, single instance is provided

Class diagram – sourced from Gang of Four.
Designing a singleton class from creational patterns category particularly ‘The lazy initialization type’ and that is thread-safe comes with its own race-condition challenges, hence programmers must ensure while designing such a class that it does not lead to a race condition inside the method that provides a handle to the singleton instance requested for.
Since business of such a function by default come with critical section, we go for the traditional synchronization locking mechanism that is a ‘mutex’ and call its lock/un-lock functions around the critical section scope of the code.
Here, in this discussion we would be discussing with an example code to achieve the same in a lock-free manner, that is we never use ‘mutex’ synchronization locking mechanism, rather the best modern C++11,14 ,17 language & library features to achieve the same.
Some of the modern C++ language features employed in the example to be discussed…
- Lambda expressions (C++11/14)
- Delete methods (C++11)
- Inline variables
- Init-if
Modern C++ library features like…
- Thread class library
- Call_once function and once_flag
- Shared_ptr from memory management library
#include<iostream>
#include<thread>
#include<mutex>
#include<memory>
using namespace std;
//C++14 approach – A LOCK-FREE singleton
once_flag Flag;
class Singleton
{
private:
inline static shared_ptr<Singleton> ptr;
Singleton();
public:
Singleton(const Singleton &) = delete;
Singleton(Singleton &&) = delete;
Singleton& operator=(const Singleton &) = delete;
Singleton& operator=(Singleton &&) = delete;
static auto GetInstance();
void Destroy();
~Singleton();
};
//All definitions
Singleton::Singleton()
{
cout <<“Singleton Constructor, :” << this_thread::get_id() <<endl;
}
auto Singleton::GetInstance()
{
//—lambda definition begins….
auto Single_Instance = []()
{
if (auto* p= new(nothrow) Singleton(); p)
{
shared_ptr<Singleton> pt(p);
ptr.swap(pt);
}
else
{
ptr = nullptr;
}
return ptr;
};
//—lambda definition ends…
call_once(Flag, Single_Instance); //Invoke the lambda
return ptr;
}
void Singleton::Destroy(){if(ptr) ptr.reset();}
Singleton::~Singleton()
{
cout << “Singleton destructor…” << this_thread::get_id() <<endl;
}
//******************
void client1()
{
auto tm = Singleton::GetInstance();
if(tm)
cout << “Singleton_handle : “ << tm.get() << endl;
}
void client2()
{
auto tm = Singleton::GetInstance();
if(tm)
cout << “Singleton_handle : “ << tm.get() << endl;
}
int main()
{
auto tm = Singleton::GetInstance();
if(tm)
cout << “Singleton_handle : “ << tm.get() << endl;
thread th1(client1);
thread th2(client2);
th1.join();
th2.join();
return 0;
}
The above example if you note when test would be thread-safe lock-free singleton.
Why Singleton design pattern?
There are number scenario’s where we need to ensure in a system, there shall only a single instance of a particular type, allowing multiple instances would not really help the cause or make any sense. For e.g.
- A database Engine

- Network print spooler

- An XY or XYZ axis plane of a graph plot, no matter how many line graph instances it may contain.

The story of const objects and const methods in C++:
We talk about the relevance of the const qualifier and best practices one should adopt as a C++ developer.
In C++, the const qualifier can be applied for elements at various levels
– global-scope
– class-scope and
– local (function) scope for functions, variables, and class objects.
Here in this discussion our focus will be on the usage and importance of the const qualifier when applied for
– class objects and
– class member functions as a C++ developer
Why should we use const on class objects and thereby on certain member functions of a class?
Let us consider the following example/scenario,
We as an application developer plan to consume a class library developed by another team or vendor, and we do not have the privileges of editing or modifying this class library. This class also does not follow the const correctness aspect.
The class looks as follows…

Given the above class, let us consider two different scenarios as to how a class consumer
would go about consuming the above class in his/her application code.

Now, the second scenario:

The call to ‘print’ is an error that would be issued to the class consumer for no fault of his. This is so, because the class library vendor has overlooked the ‘const’ correctness aspect.
We say,
– A const object would only call const methods
– An object that is not const call any method, whether const or not a const
Let us dive a bit deep to understand this issue or error in the first place. The following snippet illustrates as to what the compiler translation or interpretation of our class code internally looks like as a binary.

Note, the hidden formal parameter ‘this’ is a pointer to a READ-WRITE ‘ MyClass ‘ type, hence can only accept the address of a READ-WRITE ‘ MyClass ‘ type and not a READ-ONLY type ‘const MyClass *’ type. Hence the error.
So, the ‘MyClass:print’ method’s hidden formal parameter must be a pointer to a READ-ONLY type. This is when or why the class developer should request the compiler to consider or treat the hidden formal parameter ‘this’ as READ-ONLY for the ‘MyClass:print’.
The following changes are necessary on the class end to make this happen, qualify both declaration as well as the definition of the ‘print’ method as ‘const’ on the right-hand-side.

Note:
– Once a method is marked ‘const ‘, we cannot modify the class data members in the functions scope.
– Avoid qualifying the constructor(s) and destructor method as ‘const ‘, doing so is an error for the following reasons.
The effect of the ‘const ‘ qualifier on an object only takes place after/during the epilogue phase of the constructor call and nothing earlier. And the same gets removed or neutralized during the prologue phase of the destructor call.
So, to conclude the effect the ‘const’ qualifier on an object is only between these two transitions.

The following image illustrates the same.
What are the best practices and thereby which candidate must be qualified as ‘const ‘ in a C++ code in general ?
FIRST-LEVEL of const correctness:
– If there is any member function only doing a read-only business on object’s data, qualify such functions as ‘const ‘.
SECOND LEVEL of const correctness:
The reason for copy constructors formal parameter being declared as ‘const ‘:-
– As a class library developer, if we happen to define our own custom copy constructor in a class, then we have to ensure that our copy constructor is ready to receive either a read-write or a read-only object from the consumer end.
Note: On the formal parameter received by the copy constructor, there is a READ-ONLY operation that is being attempted on the formal parameter. Thereby, our custom copy constructor should welcome either a non-const object or a const object.
Here ‘x’ is an alias to a READ-WRITE CA kind, acts as an alias only to non-const objects, which is not good.

Here ‘x’ is an alias to a READ-ONLY MyClass kind, acts as an alias to both const as well as non-const objects, a good idea.

THIRD-LEVEL of const correctness: –

Hope, by now we have understood the importance or relevance of ‘const’ correctness in a C++ code.