Article 11 Delete functions are preferred over private undefined functions

Posted by mraiur on Sat, 12 Feb 2022 04:36:29 +0100

Delete functions are preferred over private undefined functions

If you write code for other programmers and you want to prevent them from calling a specific function, just don't declare the function. Function is undeclared and cannot be called. But sometimes c + + will declare functions for you, and if you want to prevent these clients from calling these functions.

This only happens to "special member functions", that is, member functions automatically generated by c + + when needed.

In order to prevent these functions from being used, C++98 declares them private and does not define them.

In C + +, it is not advisable to copy the input stream and output stream. To make the input and output streams non replicable.

Basic in C++98_ IOS is defined like this

template<class charT, class traits=char_traits<charT>>
class basic_ios : public ios_base{
public:
    ...
private:
    basic_ios(const basic_io&);		//not defined
    basic_ios& operator=(const basic_ios&);		//not defined
};

By declaring these functions private, clients are prevented from calling them. Deliberately not defining them means that if a piece of code can still access them (such as member functions or friends of classes) and use them, the linking phase will fail due to the lack of function definitions.

In C++11, there is a better way to achieve the same result: use = delete to identify the copy constructor and copy assignment operator as deletion functions.

template<class charT, class traits=char_traits<charT>>
class basic_ios : public ios_base{
public:
	...
    basic_ios(const basic_ios&) = delete;
    basic_ios& operator=(const basic_ios&) = delete;
    ...
};

Deleting functions cannot be used in any way, so even the code in member and friend functions will be affected by trying to copy basic_ios can not work, which is an improvement for C++98, because in C++98, the latter improper use can not be diagnosed until the link stage.

Traditionally, delete functions are declared public rather than private. When the client code tries to use a member function, C + + will check the accessibility first and then the deletion status. In this way, when the client code tries to call a private deletion function, some compilers will only complain that the function is private, although the accessibility of the function does not affect its availability.

An important advantage of delete function is that any function can become a delete function, but only member functions can be declared private.

For example, suppose there is a non member function that takes an integer and returns whether it is a lucky number:

bool isLucky(int number);

The C origin of C + + determines that all types that can be regarded as values can be implicitly converted to int, but some calls can be compiled, but they are semantically meaningless.

if(isLucky('a'));		//Is' a 'a lucky number?

if(isLucky(true));		//Is true a lucky number?

if(isLucky(3.5));		//It should be truncated to 3 before checking whether it is a lucky number

If the lucky number must be an integer, prevent the above call from compiling.

One way is to create and delete overloaded versions of the types we want to filter out.

bool isLucky(int number);		//Original version

bool isLucky(char) = delete;	//Reject char type

bool isLucky(bool) = delete;	//Reject bool type

bool isLucky(double) = delete;	//double and float types are rejected

When float type is faced with the choice of transforming to int or double type, C + + will preferentially transform to double type. For the call of isLucky, if float is passed in, the overloaded version of double type will be called instead of the one of int. It can only be said that it will try to call the overloaded version of the double type parameter, but the compilation is blocked because the overloaded version is already a deleted version.

Although deletion functions cannot be used, they are still part of the program, so they will be taken into account when overloading resolutions.

if(isLucky('a'));		//error

if(isLucky(true));		//error

if(isLucky(3.5f));		//error

Function implementations that should not be able to be deleted.

For example, suppose you need a template that works with built-in pointers

template<typename T>
void processPointer(T* ptr);

There are two different classes in the pointer world. One is the void * pointer, which cannot be dereferenced, self increasing, self decreasing and other operations. The other is char *, because they basically represent C-style strings rather than pointers to individual characters.

In the processPointer template, it is assumed that such a special handling method is to reject the call when using these two types, that is, you can't use void * and char * to call processPointer.

Just delete these specific implementations.

template<>
void processPointer<void>(void*) = delete;

template<>
void processPointer<char>(char*) = delete;

Then, if it is illegal to use void * and char * to call processPointer, it is likely that const void * and const char * are also illegal.

template<>
void processPointer<const void>(const void*) = delete;

template<>
void processPointer<const char>(const char*) = delete;

If it is a function template inside the class, and you want to disable some specific implementations through the private declaration, you can't. Because you can't give a specialization of the member function template a different access level of the main template. If processPointer is a member function template inside the Widget, and you want to prohibit the use of void * pointer to call.

The following is the practice of c++98.

class Widget{
public:
	template<typename T>
    void processPointer(T* ptr){}
private:
    template<>					//error
    void processPointer<void>(void*);
};

The problem is that template specialization must be written within namespace scope, not class scope. This problem will not appear in deleting functions, because they do not need different access levels at all. Second, member function templates can be deleted outside the class.

class Widget{
public:
    template<typename T>
    void processPointer(T* ptr){ ... }
    
};

template<>
void Widget::processPointer<void>(void*) = delete;

Key points shorthand

  • Delete functions are preferred over private undefined functions
  • Any function can be deleted, including non member functions and template materialization