Main Content

AUTOSAR C++14 Rule A15-2-1

Constructors that are not noexcept shall not be invoked before program startup

Description

Rule Definition

Constructors that are not noexcept shall not be invoked before program startup.

Rationale

In C++, the compiler responds to an exception by following these steps:

  • The compiler tries to match the exception with a handler in the current scope or a higher scope.

  • If the exception matches with a handler, then the handler accepts the exception and begins stack unwinding. During stack unwinding, The program execution moves from the scope that produces the exception to the outer scopes in reverse order. The program execution then invokes the destructors for each variable on the stack that are not destroyed yet. After stack unwinding, program execution resumes from the line immediately after the triggered handler.

  • If the exception does not match a handler, then the compiler terminates the execution in an implementation-defined manner. That is, the exact process of program termination depends on the particular set of software and hardware that you use. For instance, the compiler might invoke std::terminate(), which in turn might invoke std::abort() to abnormally abort the execution. Based on the implementation, the stack might not be unwound before the program is aborted. If the stack is not unwound before program termination, then the destructors of the variables in the stack are not invoked, leading to resource leak and security vulnerabilities.

Before program startup, the constructors of static or global objects are invoked to construct and initialize these objects. If such a constructor raises an exception, the compiler might abnormally terminate the code execution without unwinding the stack. Consider this code where the constructor of the static object obj might cause an exception.

class A{
	A(){
		
		//...
	}	
};

static A obj;

main(){
	//...
}
The static object obj is constructed by calling A() before main() starts. Because A() is called before program startup, no exception handler can be matched with exceptions raised by A(). Based on the implementation, such an exception can result in program termination without stack unwinding, leading to memory leak and security vulnerabilities.

Because exceptions raised by constructors of static or global objects cannot be matched to an exception handler, declare these constructors as noexcept.

Polyspace Implementation

Polyspace® flags statements where non-noexcept constructors of a static or global object are directly invoked. It also highlights the noncompliant constructors.

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 constructors of static or global objects.

#include <cstdint>
#include <stdexcept>
#include <string>
class A
{
public:
	A() noexcept : x(0){}
	A(std::int32_t n) : x(n) {
		throw std::runtime_error("Unexpected error"); 
	}
	A(std::int32_t i, std::int32_t j) noexcept : x(i + j)
	{
		try {
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
		}
	}
private:
	std::int32_t x;
};

static A a1;     // Compliant
static A a2(5);  // Noncompliant
static A a6(5);  // Noncompliant
static A a3(5, 10); // Compliant
A a4(5);  //Noncompliant          
A a5(5, 10); //Compliant
int foo_A(A a) { };

int bar_A(int value) {
	A a{value};   //Compliant
	return foo_A(a);
}
int value2b = bar_A(20);   // Compliant
std::string s{"Hello World"};//Noncompliant
int value2a = foo_A(20);   //Noncompliant
int convert(){

	
	return  foo_A(a2);   
}

  • Polyspace flags the statement std::string s{"Hello World"}; because this statement invokes the non-noexcept constructor of the string s before program startup.

  • Polyspace flags the constructions of a2, a4, and a6 because these constructions invoke the non-noexcept constructor A(std::int32_t n) before program startup.

  • Polyspace does not flag the construction of a5 because this construction invokes the noexcept constructor A(std::int32_t i, std::int32_t j).

  • Polyspace flags the statement int value2a = foo_A(20); because the implicit conversion from int to A requires invoking the non-noexcept constructor A(std::int32_t n) before startup.

  • Polyspace does not flag the statement A a{value}; in the body of the function bar_A() because the object a is local, and it is not created during program startup.

Check Information

Group: Exception Handling
Category: Required, Automated

Version History

expand all