Main Content

AUTOSAR C++14 Rule A15-5-1

All user-provided class destructors, deallocation functions, move constructors, move assignment operators and swap functions shall not exit with an exception. A noexcept exception specification shall be added to these functions as appropriate

Since R2020b

Description

Rule Definition

All user-provided class destructors, deallocation functions, move constructors, move assignment operators and swap functions shall not exit with an exception. A noexcept exception specification shall be added to these functions as appropriate.

Rationale

This rule states that certain functions must not exit with an exception.

  • Destructors and deallocation functions: When an exception is raised, the compiler invokes the destructors and deallocation functions to safely delete the objects in the stack. If a destructor or a deallocation function exits with an exception at that time, the compiler terminates the program execution abnormally. Depending on the software or hardware that you use, abnormal program termination can result in resource leaks and security vulnerabilities. To prevent these issues, avoid destructors and deallocator functions that might exit with an exception. Default destructors and deallocators are noexcept functions. When you provide a custom destructor or deallocation function, specify them as noexcept and handle all exceptions within the function so that they do not exit with exceptions. For a polymorphic class hierarchy, this rule applies to the destructors of the base and all derived classes.

  • Move constructors and move assignment operators: If a move constructor or a move assignment operator exits with an exception, it cannot be guaranteed that the program will revert to the state it was before the move operation. Avoid a move constructor or a move assignment operator that might exit with an exception. Specify these functions as noexcept because standard library functions might avoid move operations unless they are declared as noexcept. You can also declare these special member functions as =default. For more information on when you can declare the special member functions as =default, see AUTOSAR C++14 Rule A12-0-1.

  • Swap functions: Developers expect that a swap function does not exit with an exception. If a swap function exits with an exception, standard library algorithms and copy operations might not work in your code as expected. Specify swap functions as noexcept. Avoid operations that might exit with an exception in swap functions.

When you use templates as generic move constructors, generic move assignment operators, and generic swap functions, these templates can have dynamic exception specifications without violating this rule.

Polyspace Implementation

Polyspace® flags a user-defined destructor, deallocation function, move constructor, move assignment operator, and swap function if it might raise an exception. If a function is named swap or Swap and takes a reference as input, Polyspace considers it a swap function.

Polyspace ignores functions that are declared but not defined.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

This example shows how Polyspace flags destructors and deallocation functions that might raise an exception. Consider this code with two classes.

#include <stdexcept>
class Compliant
{
public:
	//...
	~Compliant()  //Compliant                            
	{
		try {
			// ...
			throw std::runtime_error("Error"); 
		}
		catch (std::exception& e) {
		//...	
		}
	}
};

class Noncompliant
{
public:
	//...
	~Noncompliant() 
	{
		throw std::runtime_error("Error"); //Noncompliant    
	}
	static void operator delete(void* ptr, std::size_t sz) 
	{
		// ...
		throw std::runtime_error("Error");    // Noncompliant
	}
};
  • The destructor of Compliant raises an exception by using a throw statement. Because this exception is handled within the destructor function by using a try-catch block, ~Compliant() is compliant with this rule.

  • The destructor of Noncompliant also raises an exception by using a throw statement. Because this exception is not handled within the function, the destructor ~Noncompliant() exits with an exception. Polyspace flags this throw statement in the destructor.

  • The deallocation function Noncompliant::delete() does not comply with this rule because it does not handle the exception raised within the function. Polyspace flags the throw statement in the function.

Polyspace flags move operators or move constructors if:

  • They might exit with an exception

  • They are not specified as noexcept

Consider this code where move operations are implemented for two classes.

#include <stdexcept>
class Compliant
{
	//...
public:
	Compliant(Compliant&& rhs) noexcept   //Compliant          
	{
		try {
			// ...
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
			//...
		}
	}

	Compliant& operator=(Compliant&& rhs) noexcept //Compliant   
	{
		try {
			// ...
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
			//...
		}
		return *this;
	}	
};

class Noncompliant
{
public:
	//...
	Noncompliant(Noncompliant&& rhs) //Noncompliant 
	{
		// ...
		throw std::runtime_error("Error");    //Noncompliant
	}
	Noncompliant& operator=(Noncompliant&& rhs) //Noncompliant
	{
		// ...
		throw std::runtime_error("Error");   //Noncompliant
		return *this;
	}

};

  • The move assignment operator and move constructor of the class Compliant are specified as noexcept and these functions handle exceptions that arise within them. The move constructor and move assignment operator of Compliant are compliant with this rule.

  • The move assignment operator and the move constructor of the class Noncompliant are not specified as noexcept. Polyspace flags the declaration of these functions.

  • The move assignment operator and the move constructor of the class Noncompliant contain throw statement that raise exceptions without handling them within these functions. Polyspace flags these throw statements.

Consider this code containing two swap functions.

#include <stdexcept>
namespace Compliant{
	class C1{};
	void Swap(C1& lhs, C1& rhs) noexcept   //Compliant
	{
		// Implementation
	}
}
namespace Noncompliant{
	class C2{};
	void Swap( C2& lhs, C2& rhs ) noexcept(false) //Noncompliant
	{
		throw std::runtime_error( "Error" );   //Noncompliant
	}
}
  • The function Compliant::Swap() is specified as noexcept and does not raise an exception. This swap function is compliant with this rule.

  • The function Noncompliant::Swap() is specified as noexcept(false) and it exits with an exception. Polyspace flags the exception specification of the function and the throw statement.

Check Information

Group: Exception handling
Category: Required, Automated

Version History

Introduced in R2020b