9

At a reply of a blog post of Raymond Chen,

A questioner pointed out

Raymond, I believe the C++ example is not correct since the position of the base class subobject in the derived class is unspecified according to ISO C++ 2003 Standard (10-3, page 168), and you assume that the base class subobject is always at the beginning. The C example would be fine in C++ too, so I'd stick with it.

Raymond replied

[The code does not make this assumption. That's why it's important to use static_cast instead of reinterpret_cast. Try it: Add a virtual method to OVERLAPPED (so a vtable goes in front) and observe what the compiler does. -Raymond]

I could guess after read his comments. Using static_cast is fine at the example but reinterpret_cast is not. Because reinterpret_cast is not convert vtable. Do I understand it rightly?
Though, if I use C-Style cast at there(not reinterpret_cast), could it also go wrong?

I re-read More Effective C++'s cast explanation to understand that. But there was no answer about that.

Benjamin
  • 10,085
  • 19
  • 80
  • 130
  • 2
    I'm not at all versed in C++, but my understanding is that a "reinterpret cast" means exactly what `*(destination_type *)&` would mean in C. Presumably "static cast" actually takes class relationships into account and allows the compiler to do non-trivial conversion work. – R.. GitHub STOP HELPING ICE Feb 04 '12 at 05:51

1 Answers1

17

Using static_cast is fine at the example but reinterpret_cast is not. Because reinterpret_cast is not convert vtable.

No, the problem is that the reinterpret_cast is completely oblivious about the inheritance. It will simply return the same address unchanged1. But static_cast knows that you're performing a downcast: i.e. casting from a base class to a derived class. Since it knows both types involved it adjusts the address accordingly, i.e., does the right thing.

Let's pretend our implementation lays out the hypothetical OVERLAPPEDEX class that has a virtual function like this:

+------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------------+------------------+-------------+
       ^
       |
      ptr

The pointer we're given points to the OVERLAPPED subobject. reinterpret_cast would not change that. It would only change the type. Obviously, accessing the OVERLAPPEDEX class through this address would easily wreak havoc, because the locations of its subobjects are all wrong now!

       what we believe we have when we access OVERLAPPEDEX through the pointer
       +------+------------+------------------+-------------+
       | vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------+-----+------+-----------+------+------+------+
| vptr | OVERLAPPED | AssociatedClient | ClientState | <- what we actually have
+------+------------+------------------+-------------+
       ^
       |
      ptr

static_cast knows that to convert a OVERLAPPED* to OVERLAPPEDEX* it must adjust the address, and does the right thing:

 +------+------------+------------------+-------------+
 | vptr | OVERLAPPED | AssociatedClient | ClientState |
 +------+------------+------------------+-------------+
 ^
 |
ptr after static_cast

Though, if I use C-Style cast at there(not reinterpret_cast), could it also go wrong?

A C-style cast is defined as the first one of the following that succeeds:

  1. const_cast
  2. static_cast
  3. static_cast, then const_cast
  4. reinterpret_cast
  5. reinterpret_cast, then const_cast

As you can see, a static_cast is tried before reinterpret_cast, so in this case, a C-style cast would also do the right thing.


More info


1Not guaranteed. There are very little guarantees about what happens on a reinterpret_cast. All implementations I know of will simply give out the same address unchanged.

Community
  • 1
  • 1
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Yes, my `convert vtable` was the very weird sentence. btw, Does C-style caster also know the inherit-relationship? Maybe it is not. But I have always used the C-style cast, even if I use C++. And I think it even worked well at the example. That's weird thing. – Benjamin Feb 04 '12 at 06:10
  • 1
    @Benjamin I added an explanation about C-style casts, and why it does work in this case. I would recommend against use them though, because it isn't always clear which of the alternatives is picked. A C-style case is a very blunt hammer. – R. Martinho Fernandes Feb 04 '12 at 06:14
  • Of course I will follow your recommend, because I understand it now. Thank you. Does your order of C-Style caster doing is standardized, though? – Benjamin Feb 04 '12 at 06:19
  • 1
    @Benjamin yes, that order is specified by the standard. – R. Martinho Fernandes Feb 04 '12 at 06:21
  • Also note a possible gotcha with the C-style cast: If the compiler can't determine that there's an inheritance structure (for example missing a header include) it will fall back to `reinterpret_cast`, failing in just the same way. This is yet another reason C-casts are dangerous. – Mark B Feb 04 '12 at 06:56
  • I'm curious about your footnote. Does the standard allow for `reinterpret_cast` to change the value? How could that ever be useful? It seems like it flies in the face of what `reinterpret_cast` is designed to do. – StilesCrisis Feb 04 '12 at 07:18
  • @StilesCrisis: what do you think `reinterpret_cast` is designed to do? Yes, the standard allows it to change the value. The only guarantee you have is that if you `reinterpret_cast` back to the original type you get the original value. The intermediate value can be *anything*, as long as the implementation can retrieve the original from it. If you don't rely on the implementation-defined behaviour of `reinterpret_cast` that's all you can do with it: get an unspecified value of another type, and then retrieve the original from it. – R. Martinho Fernandes Feb 04 '12 at 07:22
  • @R.MartinhoFernandes I think Raymond had a mistake, though. The sentence `Add a virtual method to OVERLAPPED` should be `Add a virtual method to OVERLAPPEDEX`. If I add a virtual method to **OVERLAPPED**, there will be no problem even if I use reinterpret_cast. Am I right? – Benjamin Feb 05 '12 at 16:20
  • @Benjamin Oh, I didn't notice that. In that case it will probably work, (but the standard makes no guarantees). I wouldn't use it (because `static_cast` is *guaranteed* to work), but I guess it works on most implementations. – R. Martinho Fernandes Feb 05 '12 at 18:38