4

In C++, is it Undefined Behavior if a Base class object is instantiated as a base object, and subsequently downcast to a derived object?

Of course, I would assume it definitely must be undefined behavior, because the Derived class object might have member variables which the base class doesn't. So these variables wouldn't actually exist if the class was instantiated as a base object, which means that accessing them through a Derived class pointer would have to cause Undefined Behavior.

But, what if the Derived class simply provides extra member functions, but doesn't include any further member data? For example:

class Base
{
    public:
    int x;
};

class Derived : public Base
{
    public:
    void foo();    
};

int main()
{
    Base b;
    Derived* d = static_cast<Derived*>(&b);
    d->foo(); // <--- Is this undefined behavior?
}

Does this program cause undefined behavior?

Channel72
  • 24,139
  • 32
  • 108
  • 180

4 Answers4

7

Yes, it's still undefined behavior, because you're lying to the compiler about the real type of d.

See the standard 5.2.9/8:

An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cvqualification as, or greater cvqualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

The final two sentences say that if the B pointed to by the pointer is not actually part of a D derived class, the cast is undefined behavior.

Mark B
  • 95,107
  • 10
  • 109
  • 188
4

The C++03 standard, par. 5.2.9.8 lays it out (emphasis mine):

An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

Jon
  • 428,835
  • 81
  • 738
  • 806
1

Yes, that is completely undefined behaviour. That's why when downcasting you should favour dynamic_cast unless you're very very sure.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 2
    Note that, in this example, since there are no virtual function `dynamic_cast` won't work. – Mark B May 11 '11 at 17:13
0

Given the mental model I have of C++ implementation in terms of generated machine code I'd say that if the method called is not virtual and the derived class is not introducing virtual methods when the base class has none, and multiple inheritance is not involved in this trickery and and and ... it should work as you expect if the method code is indeed only accessing members defined in base object.

However this is still clearly UB in C++.

6502
  • 112,025
  • 15
  • 165
  • 265
  • Well....it may work for a really trivial case - but it won't necessarily work if you have multiple inheritance or more than one layer of inheritance etc. – Richard Corden May 11 '11 at 18:26
  • Yeah... anything not trivial could imply that the base object isn't at offset 0 in the derived. I edited my reply. – 6502 May 11 '11 at 19:24