2

Is the following C++ code correct?

struct Base { int x; };
struct Derived : Base { int y; }
Base * b = new Base;
Derived * d = static_cast<Derived *>(b);
//below we access only d->x, but not d->y
std::cout << d->x;

If not, what exactly is wrong? What C++ standard say about this? At least I have not seen it ever crashed.

user4581301
  • 33,082
  • 7
  • 33
  • 54
Koban
  • 463
  • 1
  • 6
  • 12
  • `x` and `y` are private here. – François Andrieux Sep 13 '17 at 19:12
  • 2
    `Base b = new Base;` gives you a `Base`. You can't just magically get a `Derived` out of it. – NathanOliver Sep 13 '17 at 19:13
  • And just because this simple example works doesn't mean you can rely on it working for other cases. – Anon Mail Sep 13 '17 at 19:13
  • 2
    The rules for when a `static_cast` is well defined can be found here : http://en.cppreference.com/w/cpp/language/static_cast – François Andrieux Sep 13 '17 at 19:14
  • @NathanOliver actually question is not so obvious, OP mention that he only going to access `Base` members through `d` – Slava Sep 13 '17 at 19:16
  • This works because the common layout has the derived class after the base class in memory. But the compiler could behave differently. Besides, this is a trap waiting to spring. The next coder in line may not notice the Derived is not safe to use. – Sean Perry Sep 13 '17 at 19:27
  • Regardless of how `Base` and `Derived` are laid out in memory, the compiler knows how they are related to each other, and how it needs to adjust a `Derived` pointer so it can reach a `Base` object to access its members. And since `d` does not point at a valid `Derived` object, that adjustment will result in undefined behavior. – Remy Lebeau Sep 13 '17 at 22:10

1 Answers1

11

This is reasonably straightforward in [expr.static.cast]/11 (emphasis mine):

A prvalue of type “pointer to cv1 B”, where B is a class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a class derived from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If B is a virtual base class of D or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), the program is ill-formed. The null pointer value is converted to the null pointer value of the destination type. If the prvalue 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 behavior is undefined.

You don't have a subobject of a Derived, so it's undefined behaviour.


Note there are no special cases for lvalue or xvalue pointers, and /8 mentions that the operand undergoes an lvalue-to-rvalue conversion.

chris
  • 60,560
  • 13
  • 143
  • 205