2

Why does static cast allow an upcast or downcast between pointers to objects derived or base as below, but in the case of casting between a char* and int* or vice versa int* to char*, there is a compilation error?

Casting between different pointers to objects is just as bad I believe.

// compiles fine
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * bc = static_cast<Derived*>(a);

// Gives an invalid static cast error during compilation
char charVar = 8;
char* charPtr = &charVar;
int* intPtr = static_cast<int*>(charPtr);
Zoe
  • 27,060
  • 21
  • 118
  • 148
Engineer999
  • 3,683
  • 6
  • 33
  • 71
  • 3
    *Casting between different pointers to objects is just as bad I believe* Why do you believe that? – NathanOliver Jun 04 '18 at 16:15
  • 7
    There is a relationship between `Base*` and `Derived*`. No such relationship exists between `char*` and `int*`. – R Sahu Jun 04 '18 at 16:15
  • @NathanOliver , if we cast a base object to a derived object. It is casting an "incomplete" object to an object with more data and functions? It will potentially cause a runtime error? Shouldn't this be caught at compile time? – Engineer999 Jun 04 '18 at 16:19
  • interesting, a down vote? Isn't this a valid concern for a C++ beginner? – Engineer999 Jun 04 '18 at 16:21
  • 2
    @Engineer999 That is really hard to detect. Yes, down casting can cause an issue but if I know that I went from a `Derived*` to a `Base*` I should be able to go right back without having to pay for a `dynamic_cast`. C++ doesn't do a lot of hand holding so unfortunately it leaves you with plenty of ways to shoot yourself in the foot. – NathanOliver Jun 04 '18 at 16:23
  • 1
    I'm voting to close this question as opinion-based. Questions asking _"Why such feature exists but this similar one doesn't?"_ are less than probable to be answerable with a rational, leaving us with opinions only. – YSC Jun 04 '18 at 16:34
  • 1
    I agree with YSC - perhaps the question as stated isn't really the intended question? Asking "why does `static_cast` allow {one thing} but not {different thing}" is unlikely to have an answer beyond "It was proposed and voted into the standard". – Drew Dormann Jun 04 '18 at 16:38
  • 1
    @Engineer999 There exists cases where casting from `Base*` to `Derived*` is legal and useful, such as when the `Base` pointer is known to actually point to a `Derived`. Thus, that conversion is allowed. The way it's used here is incorrect, but the compiler is not required to detect all types of errors in c++. There is no case where you can meaningfully `static_cast` from `char*` to `int*`, the only explicit cast allowed here seems to be `reinterpret_cast`, but using `intPtr` would still be problematic. – François Andrieux Jun 04 '18 at 16:57
  • IMHO: because `static_cast` can only convert between related types. `char *` and `int *` are not related. – 1201ProgramAlarm Jun 04 '18 at 19:18

3 Answers3

2

Why does static cast allow an upcast ...

There is no reason to prevent upcast. In fact, a derived pointer is even implicitly convertible to a base pointer - no cast is necessary (except in convoluted cases where there are multiple bases of same type). A derived class object always contains a base class sub object.

Upcasting is particularly useful because it allows runtime polymorphism through the use of virtual functions.

or downcast between pointers to objects derived or base as below

A base pointer may point to a base sub object of a derived object, as a consequence of an upcast. Like for example here:

Derived d;
Base *b = &d;

There are cases where you may want to gain access to members of the derived object that you know is being pointed at. Static cast makes that possible without the cost of run time type information.

It is not possible in general for the compiler to find out (at compile time) the concrete type of the pointed object (i.e. whether the pointer points to a sub object and if it does, what is the type of the container object). It is the responsibility of the programmer to ensure that the requirements of the cast are met. If the programmer cannot prove the correctness, then writing the static cast is a bug.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Ok thanks. I see the benefit of being able to cast a base pointer to a derived pointer for use in dynamic binding with virtual functions, where the base pointer actually will then point to a full derived object but the opposite, having a derived pointer pointing to a base object ? – Engineer999 Jun 04 '18 at 16:59
  • 1
    @Engineer999 You can't have a pointer-to-derived-type pointing to an instance of the base type. You would only do this conversion when you know a pointer-to-base actually points to an instance of a derived type. – François Andrieux Jun 04 '18 at 17:00
  • @FrançoisAndrieux , but doesn't static_cast allow it? – Engineer999 Jun 04 '18 at 17:00
  • 2
    @Engineer999 `static_cast` allows you to *ask* the compiler to make that conversion, but it doesn't mean the result is legal c++. It's your job to be *certain* that the pointer to are casting from actually points to an object that can be legally pointed to by the pointer type you are converting to. In c++, it's very important to understand that, just because the compiler allows it doesn't necessarily make it legal. – François Andrieux Jun 04 '18 at 17:01
  • @FrançoisAndrieux in my code example, I still can't understand why the down-casting between the object pointers is valid, hence allowed by the compiler. Here we have a Derived* pointing to a Base object. Do you have an example in which that code could be useful. I understand having a Base* pointing to a Derived object however, for use in virtual functions resolved at compile time but not vice-versa – Engineer999 Jun 04 '18 at 17:06
  • 1
    @Engineer999 Your premise is incorrect, that cast is not valid and it can never be useful. It's undefined behavior, it's an error the compiler is not required to detect or report about. – François Andrieux Jun 04 '18 at 17:06
  • @Engineer999 Point is: the compiler cannot distinguish between the situation you gave in your example and a legal one (`Base* b = new Derived();`). C++ now won't prohibit the legal down cast just because it can be badly used as well. Police won't prohibit wood chopping just because the axe can be used for killing people as well... – Aconcagua Jun 04 '18 at 17:17
2

C++ is strongly performance oriented. So as long as there is some use case for that you can gain performance, C++ will allow you to do it. Consider std::vector: Sure, there is the safe element access via function at, which does range checking for you. But if you know that your indices are in range (e. g. in a for loop), these range checks are just dead weight. So you additionally get the (less safe) operator[] which just omits these checks.

Similarly, if you have a pointer of type Base, it could, in reality, point to an object of type Derived. If in doubt, you would dynamic_cast from Base* to Derived*. But this comes with some overhead. But if you know 100% for sure (by whatever means...) what the sub class actually is, would you want this overhead? As there is a natural (even implicit!) way from Derived* to Base*, we want to have some low-cost way back.

On the other hand, there is no such natural cast between pointers of totally unrelated types (such as char and int or two unrelated classes) and thus no such low-cost way back (compared to dynamic_cast, which isn't available either, of course). Only way to transform in between is reinterpret_cast.

Actually, reinterpret_cast comes with no cost either, it just interprets the pointer as a different type – with all risks! And a reinterpret_cast even can fail, if instead a static_cast would have been required (right to prevent the question "why not just always use ..."):

class A { int a; };
class B { };
class C : public A, public B { };

B* b = new C();
C* c = reinterpret_cast<C*>(b); // FAILING!!!

From view of memory layout, C looks like this (even if hidden away from you):

class C
{
    A baseA;
    B baseB; // this is what pointer b will point to!
};

Obviously, we'll get an offset when casting between C* and B* (either direction), which is considered by both static_cast and dynamic_cast, but not by reinterpret_cast...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
0

That's because what you're trying to do is a reinterpretation - for which there's a reinterpret_cast<> operator. static_cast<> for pointers is only used for down-casting:

  1. If new_type is a pointer or reference to some class D and the type of expression is a pointer or reference to its non-virtual base B, static_cast performs a downcast. This downcast is ill-formed if B is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of D. Such static_cast makes no runtime checks to ensure that the object's runtime type is actually D, and may only be used safely if this precondition is guaranteed by other means, such as when implementing static polymorphism. Safe downcast may be done with dynamic_cast.

See:

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

for a detailed discussion of when to use each casting operator.

einpoklum
  • 118,144
  • 57
  • 340
  • 684