Soulimane Mammar
Soulimane Mammar

Reputation: 1878

Why does static_cast allow downcasts when logically it should refuse them for safety purposes or static_cast is not about safety?

In the following example the compiler accepts the static_cast downcast resulting in undefined behavior while I thought static_cast was all about safety (that C-style casts were unable to provide).

#include <iostream>

class Base {
public:
    int x = 10;
};
class Derived1: public Base 
{
public:
    int y = 20;

};
class Derived2 : public Base 
{
public:
    int z = 30;
    int w = 40;

};

int main() {

    Derived1 d1;

    Base* bp1 = static_cast<Base*>(&d1);
    Derived2* dp1 = static_cast<Derived2*>(bp1);

    std::cout << dp1->z << std::endl; // outputs 20
    std::cout << dp1->w << std::endl; // outputs random value
}

Upvotes: 4

Views: 1705

Answers (3)

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29995

You use dynamic_cast only really when you are not sure if the cast is going to succeed and you catch exceptions or check for nullptr. However if you are sure your downcasting is going to succeed, the language allows you to use static_cast (which is cheaper). If you were wrong, that is your problem. In an ideal world every cast would succeed in 100% of the time. But we don't live in an ideal world. It's a bit like array subscript. arr[5] means "I am absolutely sure this array has at least 6 elements. Compiler doesn't need to check". If your array was smaller than you expected, that's again your problem.

Upvotes: 16

I thought static_cast was all about safety (that C style cast were unable to provide)

static_cast is safer than a C-style cast. But not because it's impossible to go wrong with it. It's safer because it's only less likely to go wrong. When we write a C-style cast, a compiler will go through this list to appease us:

When the C-style cast expression is encountered, the compiler attempts to interpret it as the following cast expressions, in this order:

  1. const_cast<new_type>(expression);
  2. static_cast<new_type>(expression), with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;
  3. static_cast (with extensions) followed by const_cast;
  4. reinterpret_cast<new_type>(expression);
  5. reinterpret_cast followed by const_cast.

The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled (see example). If the cast can be interpreted in more than one way as static_cast followed by a const_cast, it cannot be compiled. In addition, C-style cast notation is allowed to cast from, to, and between pointers to incomplete class type. If both expression and new_type are pointers to incomplete class types, it's unspecified whether static_cast or reinterpret_cast gets selected.

The point in favoring static_cast to that, is that you have a finer grained control over the resulting cast, which does grant a measure of added safety. But it doesn't change the fact that the C++ object model requires that static_cast support casting even when undefined behavior is possible. Only dynamic_cast (not on the above list, by the way) has an added bit of safety for polymorphic types, but that's not without overhead.

Upvotes: 6

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385395

I don't really know what to tell you. Why does it allow such a cast? For when you need/want one.

Don't want to use it? Don't! You could switch to dynamic_cast (more expensive), or don't cast.

C++ lets you do plenty of things that require thought and care. This is one of them.

But it is still safer than C. The static_cast won't let you cast bp1 to an UrgleBurgle*, for example.

Of course ultimately you can still use the C-style casts if you like. I mean, I wouldn't advise it, but you could. C++ is all about choice (usually between a terrible option and a slightly less terrible option).

Upvotes: 5

Related Questions