In this article we will discuss how to use delete keyword with functions and its use cases.

Deleting Functions using ‘delete’ keyword

In C++11 a new feature of keyword delete is introduced. Now we can apply the delete keyword to functions to make them uncallable.

void someFunction() = delete ;

Its practical use cases are,

  • Deleting compiler generated functions like copy constructor, assignment operators, move constructor , move assignment operator and default constructor.
  • Deleting member functions to prevent data loss conversions
  • Restrict Object creation on Heap by deleting new operator for class
  • Delete specific template specilaizations

Let’s discuss them one by one i.e.

Deleting Copy Constructor and Assignment Operator

class User
{

	int id;
	std::string name;
public:
	User(int userId, std::string userName) : id(userId), name(userName)
	{}

	// Copy Constructor is deleted
	User(const User & obj) = delete;
	// Assignment operator is deleted
	User & operator = (const User & obj) = delete;

	void display()
	{
		std::cout<<id << " ::: "<<name<<std::endl;
	}

};

In the above class User, Copy Constructor and Assignment Operator are deleted. If anybody tries to call the deleted function then it will generate compile time error.
For example,
User userObj(3, "John");

User obj = userObj;

As copy constructor is deleted, therefore above lines will throw following compile time error i.e.
delete_example.cpp: In function ‘int main()’:
delete_example.cpp:136:13: error: use of deleted function ‘User::User(const User&)’
  User obj = userObj;
             ^
delete_example.cpp:111:2: note: declared here
  User(const User & obj) = delete;

A part from applying delete keywords to compiler generated functions, we can apply delete keyword to other functions too.

Deleting member functions to prevent data loss conversions

Some times due to automatic type conversion we can call the functions with wrong parameters too. For example, for User class we have this parameterized constructor i.e.

User(int userId, std::string userName) : id(userId), name(userName)
{}

Even if this constructor accepts id as int, still we can call this by double 7 char too i.e.
User obj4(5.5, "Riti");

User obj5('a', "Riti");

It will work but these are invalid conversions, with delete keyword we can prevent them too by declaring them deleted i.e.
// Deleting a constructor that accepts a double as ID to prevent narrowing conversion
User(double userId, std::string userName) = delete ;

// Deleting a constructor that accepts a double as ID to prevent invalid type conversion
User(char userId, std::string userName) = delete ;

Now on creating User object with double and char will throw compile time error i.e.
error: use of deleted function ‘User::User(double, std::__cxx11::string)’
  User obj4(5.5, "Riti");
                      ^
note: declared here
  User(double userId, std::string userName) = delete ;

error: use of deleted function ‘User::User(char, std::__cxx11::string)’
  User obj5('a', "Riti");
                       ^
note: declared here
  User(char userId, std::string userName) = delete ;

Restrict Object creation on Heap by deleting new operator for class

We can delete the new operator of class User i.e.

class User
{
	int id;
	std::string name;
public:
	User(int userId, std::string userName) : id(userId), name(userName)
	{}

	// Delete the new function to prevent object creation on heap
	void * operator new (size_t) = delete;

};

 

Now if we try to create User Object on heap using new, it will give compile time error i.e.

User * ptr = new User(1, "Riti");

Compile Error:
error: use of deleted function ‘static void* User::operator new(size_t)’
  User * ptr = new User(1, "Riti");
                                 ^
note: declared here
  void * operator new (size_t) = delete;

Complete example is as follows,
#include <iostream>
#include <string>

class User
{

	int id;
	std::string name;
public:
	User(int userId, std::string userName) : id(userId), name(userName)
	{}

	// Copy Constructor is deleted
	User(const User & obj) = delete;
	// Assignment operator is deleted
	User & operator = (const User & obj) = delete;

	void display()
	{
		std::cout<<id << " ::: "<<name<<std::endl;
	}

	// Deleting a constructor that accepts a double as ID to prevent narrowing conversion
	User(double userId, std::string userName) = delete ;

	// Deleting a constructor that accepts a double as ID to prevent invalid type conversion
	User(char userId, std::string userName) = delete ;

	// Delete the new function to prevent object creation on heap
	void * operator new (size_t) = delete;

};


int main()
{
	User userObj(3, "John");

	// Can not copy User object as copy constructor is deleted
	//User obj = userObj;


	/*
	 * Creating User objects with double or char as ID will cause compile time error
	 *
	User obj4(5.5, "Riti");

	obj4.display();

	User obj5('a', "Riti");

	obj5.display();
	 */

	// Can not create object on heap as new operater is deleted

	//User * ptr = new User(1, "Riti");

	return 0;
}

Delete specific template specialisation

With delete keyword we can restrict certain template specialisations of template classes or functions. Let’s see how to do that,
Suppose we have a template class of Complex Number

template <typename T>
class ComplexNumber
{
	T x;
	T y;
public:
	ComplexNumber(T a, T b) : x(a) , y(b)
	{}
	void display()
	{
		std::cout<<x << " + i"<<y<<std::endl;
	}
};

We can use ComplexNumber with different types like int, double & char etc. i.e.
ComplexNumber<int> obj1(1,2);

ComplexNumber<double> obj2(1.0,2.0);

ComplexNumber<char> obj3('1' , '2');

Now we want to restrict that no one can use CompleNumber class with double or char as template parameter. For that we need to delete certain specialized functions i.e.
ComplexNumber(char a, char b) = delete;

ComplexNumber(double a, double b) = delete;

Now on trying to use ComplexNumber<> class with double or char as template parameter will generate compile time error.

Complete ComplexNumber class with deleted specialisations is as follows,

template <typename T>
class ComplexNumber
{
	T x;
	T y;
public:
	ComplexNumber(T a, T b) : x(a) , y(b)
	{}
	void display()
	{
		std::cout<<x << " + i"<<y<<std::endl;
	}
        // Deleted template specialisation 
	ComplexNumber(char a, char b) = delete;
        // Deleted template specialisation  
	ComplexNumber(double a, double b) = delete;
};

Different between deleted function and private functions

By making functions private we can also restrict its calling but still marking function deleted with delete keyword has its own advantages like,

  • Private member functions can be called from other member functions, whereas, deleted functions can not be called even from other member functions.
  • Deleted functions exists in name lookup , if compiler finds a function is deleted then it will not lookup for other matching functions based on type lookups, hence prevents unneccesary data loss and bugs.