0

In the below code, why does the compiler agree on casting a base class to a derived class knowing the object is purely a base one? How can print2() be called although it's a derived function? Can you tell me please what happens exactly from the point of view of a compiler/memory manager?

#include <iostream>

using namespace std;

class Base {
public :
    virtual void print(){
        cout << "Hello from Base" << endl;
    }
};

class Derived : public Base {
   public:
       void print(){
          cout << "Hello from Derived" << endl;
        }
    
       void print2(){
         cout << "Print2" << endl;
       }
 };

 int main()
{
   Base * b = new Base();
   Derived * d = static_cast<Derived *>(b);
   d -> print2();
}
  • 2
    See [this question](https://stackoverflow.com/questions/47310700/c-static-cast-downcast-validity). `static_cast` doesn't perform any runtime checks when downcasting. If you have some other guarantee that the downcast is valid, the `static_cast` is faster than the `dynamic_cast`. In your case you don't have such a guarantee so your program experiences undefined behavior. – Nathan Pierson Jun 13 '21 at 15:44
  • I see, but how on earth is print2 called? I know now that it's undefined behavior but what is this behavior that could trigger such a thing? – Hussein Jaber Jun 13 '21 at 15:48
  • I even tried dynamic_cast and it gave the same exact result – Hussein Jaber Jun 13 '21 at 15:49
  • The code doesn't cast from a base class to a derived one; it casts from a **pointer to** base class to a **pointer to** derived one. – Pete Becker Jun 13 '21 at 15:50
  • @HusseinJaber -- check the value of the pointer that you get from `dynamic_cast`. Your'e deep into undefined behavior here, and there's nothing meaningful to be said about why you get the behavior that you're seeing. Don't do that. – Pete Becker Jun 13 '21 at 15:52
  • The dynamic_cast is _also_ undefined behavior because the result of a failed `dynamic_cast` is that `d` is `nullptr` and dereferencing `nullptr` is UB. So anything _could_ happen, and since `print2` is a non-virtual function that doesn't actually use any of `Derived`'s members, what appears to happen is that it just calls the function as though you HAD invoked it from a valid `Derived*`. – Nathan Pierson Jun 13 '21 at 15:53
  • @PeteBecker Yeah. d is now a derived pointer to a base class object. Accessing print2 shouldn't happen if I'm not mistaken. However, it prints "Print2" – Hussein Jaber Jun 13 '21 at 15:53
  • @HusseinJaber --- "shouldn't happen" doesn't mean anything with code whose behavior is **undefined**. – Pete Becker Jun 13 '21 at 15:54
  • Okay so printing the d pointer after dynamic_cast is actually null (which makes sense) as you said above. – Hussein Jaber Jun 13 '21 at 15:56
  • A silly question here perhaps. shouldn't the program crash when I access a nullptr? It doesn't however – Hussein Jaber Jun 13 '21 at 15:57
  • *shouldn't the program crash when I access a nullptr?* **undefined behavior** means that anything can happen. Crash. Appear to work. Nasal demons. Anything. (FWIW, it crashes on my machine.) – Eljay Jun 13 '21 at 16:06
  • "Undefined behavior" does not mean "bad things will happen". Often it means that your code will seem to work just fine, until you're doing a demo for your most important customer. – Pete Becker Jun 13 '21 at 16:40

1 Answers1

1

This is undefined behavior, no diagnostic required.

A compiler has no obligation to report a compilation error for every possible programming bug. The only required diagnostic is when the program is ill-formed.

There's nothing technically wrong with the static_cast itself, with that statement alone. It follows all the requisite rules. The fact that it is used improperly, and results on the undefined behavior due to an earlier statement does not require an error message from the compiler. The new statement, and the static_cast itself could be in completely separate .cpp files (with a function call in between, one function calling new, and passing the result as a parameter to the function in the other .cpp file that does the static_cast). This is logically identical to the shown code. How could the compiler possibly be able to report an error, when it's compiling the other .cpp file, and has no knowledge of what's happening in the first one?

It is true that some compilers might be able to detect this bug and issue a warning message (possibly only with certain optimization levels enabled), but there is no obligation to do so.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148