Details the methods for functions that are explicitly set by default in C++ and for functions that have been deleted

  • 2020-05-09 18:51:57
  • OfStack

In C++11, default and deleted functions give you explicit control over whether special member functions are automatically generated. Deleted functions also provide you with a simple language to prevent problematic type promotion in the arguments of all types of functions (special member functions and ordinary member functions as well as non-member functions), which can lead to unexpected function calls.
Benefits of explicit default Settings for functions and deleted functions
In C++, if a type is not declared itself, the compiler automatically generates default constructors, copy constructors, copy assignment operators, and destructors for that type. These functions, called special member functions, enable the simple user-defined types in C++ to behave like structures in C. That is, they can be created, copied, and destroyed without any additional coding effort. C++11 introduces mobile semantics into the language and adds the mobile constructor and the mobile assignment operator to the list of special member functions that the compiler can automatically generate.
This is convenient for simple types, but complex types usually define one or more special member functions themselves, which prevents the automatic generation of other special member functions. Practical operation:

If any constructor is explicitly declared, the default constructor is not automatically generated. If a virtual destructor is explicitly declared, the default destructor is not automatically generated. If the move constructor or the move assignment operator is explicitly declared, then: Copy constructors are not automatically generated. The copy assignment operator is not automatically generated. If the copy constructor, copy assignment operator, move constructor, move assignment operator, or destructor are explicitly declared, then: Move constructors are not automatically generated. The move assignment operator is not automatically generated.

Pay attention to

In addition, the C++11 standard specifies the following additional rules:

If the copy constructor or destructor is explicitly declared, the automatic generation of the copy assignment operator is discarded. If the copy assignment operator or destructor is explicitly declared, the automatic generation of the copy constructor is discarded. In both cases, Visual Studio will continue to implicitly and automatically generate the required functions without warning.

The results of these rules may also leak into the object hierarchy. For example, if for some reason a base class cannot have a default constructor that can be called from a derived class - that is, an public or protected constructor that takes no arguments - then a class derived from the base class cannot automatically generate its own default constructor.

These rules can complicate the implementation of what should be straightforward content, user-defined types, and common C++ conventions -- for example, by replicating constructors and assignment operators in a private way, without defining them, making user-defined types unrepeatable.


struct noncopyable
{
 noncopyable() {};

private:
 noncopyable(const noncopyable&);
 noncopyable& operator=(const noncopyable&);
};

Before C++11, this code snippet was a convention for types that could not be copied. However, it has several problems:
The copy constructor must be declared privately to hide it, but because it is fully declared, it prevents default constructors from being automatically generated. If you need a default constructor, you must explicitly define one (even if it does nothing).
Even if the explicitly defined default constructor does nothing, the compiler will treat it as important. This is less efficient than the automatically generated default constructor and prevents noncopyable from becoming a true POD type.
Although copy constructors and copy assignment operators are hidden in the external code, member functions and friends of noncopyable can still be seen and called. If they are declared but not defined, calling them causes a linker error.
Although this is a widely accepted convention, the intent is unclear unless you understand all the rules used to automatically generate special member functions.
In C++11, non-replicable idioms can be implemented in a more direct way.


struct noncopyable
{
 noncopyable() =default;
 noncopyable(const noncopyable&) =delete;
 noncopyable& operator=(const noncopyable&) =delete;
};

Please note how to resolve issues related to the C++11 convention:
You can still prevent the generation of the default constructor by declaring the copy constructor, but you can restore it by explicitly setting it to the default value.
Explicitly set default special member functions are still considered unimportant, so performance does not degrade, and noncopyable is not prevented from becoming a true POD type.
The copy constructor and copy assignment operators are common, but have been removed. Defining or calling a deleted function is a compile-time error.
For those who know =default and =delete, the intent is clear. You don't need to know the rules for automatically generating special member functions.
A similar convention exists for the creation of user-defined types that are not mobile, can only be dynamically assigned, or cannot be dynamically assigned. All of these conventions have implementations prior to C++11 that suffer from similar problems and can be resolved in a similar way in C++11 by implementing them by default and by removing special member functions.
Functions that are explicitly set by default
You can set any special member function by default -- to explicitly declare that the special member function USES the default implementation, define a special member function with a non-public access qualifier, or restore a special member function that is otherwise prevented from being automatically generated.
Special member functions can be set by default by declaration as shown in this example:


struct widget
{
 widget()=default;

 inline widget& operator=(const widget&);
};


inline widget& widget::operator=(const widget&) =default;

Note that a special member function can be set by default outside the class body as long as it is inlined.
Because of the performance advantages of normal special member functions, we recommend that you choose to automatically generate special member functions rather than empty function bodies when you need default behavior. You can do this by explicitly default setting a special member function, or by not declaring it (or any other special member function that will prevent it from being automatically generated).
Pay attention to
Visual Studio does not support the default mobile constructor or mobile assignment operator as C++11 standard authorization. For more information, see the "default functions and deleted functions" section 1 in support of C++11/14/17 functionality (modern C++).
Deleted function
You can remove special member functions as well as ordinary and non-member functions to prevent them from being defined or called. By removing special member functions, you can more succinctly prevent the compiler from generating unwanted special member functions. Functions must be deleted when they are declared; You can't delete a function after that by declaring it and then not using it anymore.


struct widget
{
 // deleted operator new prevents widget from being dynamically allocated.
 void* operator new(std::size_t) = delete;
};

Removing normal or non-member functions prevents problematic type promotion from leading to calls to unexpected functions. This works because the deleted function still participates in the overload decision and provides a better match than the function that might be called after the promoted type. Function calls resolve to more specific but deletes functions and cause compiler errors.


// deleted overload prevents call through type promotion of float to double from succeeding.
void call_with_true_double_only(float) =delete;
void call_with_true_double_only(double param) { return; }

Note that in the previous example, calling float with the call_with_true_double_only parameter would cause a compiler error, but calling int with the call_with_true_double_only parameter would not cause a compiler error. In the int example, this parameter is promoted from int to double and the double version of the function is successfully called, even though this may not be the intended purpose. To ensure that any call to this function with a non-double parameter causes a compiler error, you can declare the template version of the deleted function.


template < typename T >


void call_with_true_double_only(T) =delete; //prevent call through type promotion of any T to double from succeeding.

void call_with_true_double_only(double param) { return; } // also define for const double, double&, etc. as needed.


Related articles: