Reputation: 377
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
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.
It is Undefined Behavior.
Any behavior is allowed for this code, including a segmentation fault.
Upvotes: 3
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