Main Content

Unnecessary implementation of a special member function

Implementing special member functions hinders code optimization when implicit versions are equivalent

Since R2023a

Description

Polyspace® reports this defect if you implement a special member function in your class with the same functionalities as the implicit version of the function. The implicit special member functions typically have these properties:

  • The implicit default constructor has an empty body, an empty parameter list, and an empty initializer list.

  • The implicit destructor has an empty body.

  • The implicit copy constructor copies the base classes and nonstatic data members by using an initializer list. The implicit move constructor moves the base classes and nonstatic data members using an initializer list. The implicit copy and move constructors do not create deep copies and do not move the data associated with a raw pointer.

  • The implicit copy and move assignment operators use base and member-wise assignments.

For details about how implicitly defined special member functions behave, see:

Risk

Compilers optimize your code by turning implicit special member functions into trivial functions, which are faster to execute. This optimization does not take place if you implement a special member function in your code. Implementing a special member function that you can omit or declare as =default results in slower code.

Unnecessary implementation of a special member function makes your code more complex, hinders optimization, and makes the code harder to read and maintain.

Fix

To fix this defect, take one of these actions:

  • Omit the implementation of the flagged special member function.

  • Declare the special member function as =default.

Performance improvements might vary based on the compiler, library implementation, and environment that you are using.

Examples

expand all

In this example, you define the class A. In this class:

  • Because the user-defined copy and move constructors of A do not manage any resources and they can be defined as =default or left undefined, Polyspace reports defects for these functions. Leaving these functions undeclared or declaring them as =default allows the compiler to turn these operations into trivial operations, resulting in more efficient code.

  • The user-defined default constructor has a nonempty initializer list. Because this default constructor initializes two data members, an implicit default constructor cannot replicate its functionalities. Similarly, an implicit constructor cannot replace the nondefault constructor. Polyspace does not report defects for these constructors.

#include <cstdint>
#include <utility> //for std::move
class A
{
public:
	A() : x(0), y(0) {}                                   // Not a defect
	A(std::int32_t first, std::int32_t second)            // Not a defect
	: x(first), y(second) {}       
	A(const A& oth) : x(oth.x),y(oth.y){}                 // Defect
	A(A&& oth): x(std::move(oth.x)),y(std::move(oth.y)){} // Defect
	~A(){}                                                // Defect


private:
	std::int32_t x;
	std::int32_t y;
};
Correction — Avoid Implementing Unnecessary Special Member Functions

To resolve the defects, declare the flagged special member functions a =default.

#include <cstdint>
#include <utility>
class A
{
public:
	A() : x(0), y(0) {}                                   // Not a defect
	A(std::int32_t first, std::int32_t second)            // Not a defect
	: x(first), y(second) {}       
	A(const A& oth)   =default;                           // Resolved
	A(A&& oth)=default;                                   // Resolved
	~A()=default;                                         // Resolved


private:
	std::int32_t x;
	std::int32_t y;
};

When your class manages resources such as a raw pointer or a POSIX file descriptor, the implicit special member functions are typically insufficient. In such cases, to avoid unexpected behavior, the best practice is to implement all the special member functions. In this example, the class A manages a raw pointer *p. To avoid unexpected behavior, implement special member functions that perform memory operations. For instance, the copy constructor in this code performs a deep copy and the move constructor invokes std::move(). Because these member functions perform actions that the implicit member functions do not perform, Polyspace does not report a defect.

#include <iostream>
#include<utility>
class A{
    private:
    int* p;
    public:
    A(){
        p = new int;
    }    
    A(int in){
        p = new int;
        *p = in;
    }
    ~A(){
        delete p;
    }
    A(const A& oth){
        p = new int;
        *p = *(oth.p);
    }
    A(A&& oth){
        p = new int;
        *p = std::move(*oth.p);
    }
};

In this example, the function foo() constructs a const instance of myclass. Because the code does not initialize myclass::x in the class, the implicit default constructor cannot correctly construct a const instance myclass. When you use compilers such as gcc or clang, declaring myclass()=default; results in a compile fail.

Providing a user-defined constructor that is identical to the implicit constructor to resolve this issue is inefficient. In this code, the constructor myclass::myclass() is identical to an implicit constructor. This constructor resolves the compilation issue, but this solution is inefficient and Polyspace reports a defect.

class myclass{
	public:
	//myclass()=default;// Compile fail
	myclass(){}         //Defect
	int x;
};

void foo(){
const myclass obj;
}
Correction — Refactor myclass to Use Implicit Constructor

To allow implicit default construction of const instances of myclass, initialize myclass::x in the class definitions.

class myclass{
	public:
	myclass()=default;// Resolved
	int x=0;
};

void foo(){
const myclass obj;
}

Result Information

Group: Performance
Language: C++ (C++11 or later)
Default: Off
Command-Line Syntax: UNNECESSARY_IMPL_OF_SPECIAL_MEMBER_FUNCTION
Impact: Low

Version History

Introduced in R2023a