Main Content

Expensive dynamic_cast

Expensive dynamic_cast is used instead of more efficient static_cast or const_cast

Since R2021b

Description

This defect is raised when dynamic_cast is used on a pointer, which is then immediately dereferenced. For instance:

std::iostream* iostream_ptr;
//...
std::string str = dynamic_cast< std::stringstream* >( iostream_ptr )->str();
The iostream pointer iosreeam_ptr is cast into a stringstream pointer, and then immediately dereferenced. Such use implies that the casting always succeeds. When you know that a casting operation succeeds, static_cast or const_cast are a more efficient choice.

Risk

When casting one class to another in a polymorphic hierarchy, you might want to use dynamic_cast when run-time type of the most derived class in the hierarchy is unknown. The dynamic_cast is more powerful because it checks the type of the argument at run time and reports error if the check fails. These functionalities make dynamic_cast a more expensive operation than either of static_cast or const_cast. Because dynamic_cast can achieve many different goals, its use might hide the developers intent when casting. Using dynamic_cast when cheaper or more explicit casting operations might be more appropriate results in code that is inefficient and more difficult to maintain. Because such code compiles and runs correctly, the inefficiency might remain undetected.

Fix

To fix this defect, replace the dynamic_cast with a more appropriate cheaper option. For instance:

  • When calling virtual functions in a polymorphic base class, remove any casting operation.

  • When downcasting from a base class to derived class, use static_cast if the casting operation succeeds in all conditions.

  • When sidecasting from one base class to another base class, use static_cast if the casting operation succeeds in all conditions.

  • When upcasting from a derived class to a base class, use static_cast.

  • To modify the const or volatile qualifiers of an object, use const_cast.

  • Refactor your code to remove inappropriate casting such as casting between unrelated classes.

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

Examples

expand all

#include <cstddef> 
#include <sstream> 
#include <string>
// downcast using `dynamic_cast` with pointer,
// and unconditionally call member function
void Downcast_NC( std::iostream* iostream_ptr )
{
	// ...
	std::string str = dynamic_cast< std::stringstream* >( iostream_ptr )->str();
	// ...
}

In this example, an std::iostream* object is cast into a std::stringstream* object by calling dynamic_cast. After the casting, the cast pointer is immediately dereferenced. Polyspace® flags the dynamic_cast.

Correction

To fix this defect, replace dynamic_cast by using static_cast. Because static_cast does not check whether a casting operation might fail, consider adding an assert statement to check if the conversion fails. After completing the development and debugging, you might remove the assert statement.

#include <cstddef> 
#include <sstream> 
#include <string>
void Downcast_C( std::iostream* iostream_ptr ) 
// if `dynamic_cast` may fail
{
	// ...
	assert( dynamic_cast< std::stringstream* >( iostream_ptr ) != NULL );//Only for debugging
	std::string str = static_cast< std::stringstream* >( iostream_ptr )->str();
}
class A{
	//...
public:
	virtual void func_A() ;
};
class B{
	//...
public:
	virtual void func_B() ;
};
class C: public A, public B{/**/};	
void foo(A& a){
	
	dynamic_cast<B*> (&a)->func_B();//Noncompliant
}
void bar(C& c){
	
	dynamic_cast<B*> (&c)->func_B();//Noncompliant
}

In this example, the class C is derived from A and B. The function foo casts the A& object a into a B* type to access the member function B::func_B. The casting operation calls dynamic_cast, which is inefficient. Polyspace flags this conversion.

The casting from the C& object to its base class, as shown in the function bar, always succeeds. Using dynamic_cast for this operation is unnecessary and Polyspace flags the conversion.

Correction

Sidecasting through an unknown most derived class is a unique capability of dynamic_cast. If your code cannot function properly without a sidecast, use dynamic_cast and justify the defect. See Annotate Code and Hide Known or Acceptable Results.

In some cases, it might be more efficient to replace a sidecast with a downcast. For instance, the function foo uses sidecasting for the explicit purpose of accessing the members of another branch in a class hierarchy. In such cases, it might be more efficient to perform the casting through the most derived class. Instead of using dynamic_cast to cast a into a B* object, you might use static_cast to cast a into a C* object. Such a downcast enables access to B::func_B while making the code more efficient. To check if the static conversion succeeds in all conditions, use assert statements during development and debugging.

Upcasting from a derived class to a base class, as shown in the function bar, always succeeds. Use static_cast for such conversions. In the preceding code, because func_B is a public virtual function, invoke the function directly by using the pointer to the class C. Casting is not necessary in this case.

class A{
	//...
public:
	virtual void func_A() ;
};
class B{
	//...
public:
	virtual void func_B() ;
};
class C: public A, public B{/**/};	
void foo(A& a){
	
	static_cast<C*> (&a)->func_B();//Compliant
}
void bar(C& c){
	
	c.func_B();//Compliant
}

Result Information

Group: Performance
Language: C++
Default: Off
Command-Line Syntax: EXPENSIVE_DYNAMIC_CAST
Impact: Low

Version History

Introduced in R2021b