Antonio
Antonio

Reputation: 377

Static cast on non-references

Consider the following program:

#include <iostream>

class Pet {};

class Cat: public Pet {
public:
    void meow() const {
        std::cout << "Meow!" << std::endl;
    }
};

int main() {
    Cat myCat;    
    Pet& myPet = myCat;
    static_cast<Cat&>(myPet).meow();
    
    // Undefined behavior?
    Pet myOtherPet;
    static_cast<Cat&>(myOtherPet).meow();
}

On my machine, the output is:

Meow!
Meow!

As expected, myPet is able to meow() just fine, since it is not just any Pet&, but it was assigned the value myCat, which is of class Cat.

However, to my surprise myOtherPet is also able to meow(). I can speculate that since meow() doesn't use any class data, the program is just letting me use my generic pet as a cat.

To make matters worse, consider the following program:

#include <iostream>

class Person {};

class Student: public Person {
public:
    float Average = 28.7;
    void print() const {
        std::cout << "Average: " << Average << std::endl;
    }
};

int main() {    
    Person& myPerson = myStudent;
    static_cast<Student&>(myPerson).print();
    
    // Undefined behavior?
    Person myOtherPerson = myStudent;
    static_cast<Student&>(myOtherPerson).print();
}

On my machine, the output is:

Average: 28.7
Average: 0

This time myOtherPerson, who is not a student, has just magically gotten Average data attached to it.

Upvotes: 0

Views: 125

Answers (2)

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29022

The two legal use cases for static_cast with references are when there is already an implicit conversion sequence or when you downcast. This is neither of these cases since myOtherPet isn't actually a Cat. You can see the legal use cases for static_assert here.

Both are undefined behavior. You are lying to the compiler by saying myOtherPet is a Cat and myOtherPerson is a Student while they are not. It might print "Meow!", it might crash, it might print arbitrary output or it might do something else.


  • Is this behavior consistent across compilers or undefined?

It is Undefined Behavior.

  • Can such use cause segmentation faults?

Any behavior is allowed for this code, including a segmentation fault.

Upvotes: 3

JDługosz
JDługosz

Reputation: 5642

Normally I think of static_cast as allowing normal implicit conversions or their inverses. So why doesn't this object, saying you have to use reinterpret_cast instead?

Read through https://en.cppreference.com/w/cpp/language/static_cast

If new_type is a reference or pointer to some class D and expression is lvalue of its non-virtual base B or prvalue pointer to it, 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 a downcast 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, ...

So, there is a base/derived relationship between the two types. You are specifying a "downcast" and the compiler takes you at your word. If the types were unrelated, the static_cast would indeed be rejected. If you want type checking, you need to use dynamic_cast instead and have virtual functions so the run-time checking is possible.

Upvotes: 1

Related Questions