1

Is this allowed?

#include <iostream>

class Foo {
public:
    int a;
    int b;
};

class Bar : public Foo
{
   // NO data members added
   public:
   inline int sum() const { return a+b;};
};

int main(){
    Foo * foo = new Foo{10,20};
    Bar * bar = reinterpret_cast<Bar *>(foo);
    std::cout << bar->sum() << std::endl;
}

Note that the above code works in GCC, CLANG and MSVC. The question is, is this undefined behaviour or is this allowed? My actual use case is a subclass of boost::shared_ptr which only adds some helper methods but the above code is a simple demonstration of concept.

See https://godbolt.org/z/zzoxj7Maz for a live version

bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
  • Memory leak! you don't delete `foo`. anyhow: this is what [`dynamic_cast`](https://en.cppreference.com/w/cpp/language/dynamic_cast) is for. – JHBonarius Jan 06 '22 at 16:28
  • Sure it's a memory leak. That is not what is under discussion. I'm asking if the cast is allowed. Note that the concrete type is not Bar but Foo. – bradgonesurfing Jan 06 '22 at 16:28
  • Can you not add these helper methods as free functions? e.g. instead of `bar->sum()` why not do `sum(foo)`? – Kevin Jan 06 '22 at 16:29
  • @Kevin This subclass of boost::shared_ptr is a legacy part of a large codebase. I can't remove it easily but would like to use boost::make_shared to initialize it. The idea is to reinterpret_cast to the child class. – bradgonesurfing Jan 06 '22 at 16:30
  • Ahh, I get it. you specifically want to reinterpret... I think it might be allowed if the object is trivial right? Or what's the word. No vtable, only trivial members. No members in de derivative... I remember just watching a youtube video about this a few days ago... from some recent conference. – JHBonarius Jan 06 '22 at 16:33
  • 1
    Ok, it was this one: [Embracing PODs Safely Until They Die - Alisdair Meredith & Nina Ranns - CppCon 2021](https://www.youtube.com/watch?v=u_fb2wFwCuc). I think they state it's legal if it's a Plain Old Data type. But maybe I'm wrong. – JHBonarius Jan 06 '22 at 16:37
  • 1
    Looks like a way to spoof *extension methods* in C++. (Wise? Let's ignore that for now. UB? Probably, but I can't cite chapter-and-verse... may want `language-lawyer` tag on this question.) – Eljay Jan 06 '22 at 16:39
  • Actually I've decided not to do this. I can simply add a couple of constructors to the child class of boost::shared_ptr which accept a boost::shared_ptr and then it works without the scary magic. – bradgonesurfing Jan 06 '22 at 16:41
  • @JHBonarius Do you have a timestamp in the video where that's said? Also, `boost::shared_ptr` isn't a POD type so it doesn't apply here anyway. – Kevin Jan 06 '22 at 16:42
  • Just write a standalone function. – n. m. could be an AI Jan 06 '22 at 16:47
  • @Kevin I don't know the presentation by heart and have no time to rewatch it. But also, it's not as simple as "a timestamp", because this is not a trivial thing. The **_whole_** presentation is important, as it discusses all requirements and conditions that need to be fulfilled for any guarantees on binary compatibility. I do remember they give an example where inheritance is involved. – JHBonarius Jan 07 '22 at 07:40
  • @JHBonarius I watched the first half of it or so. The only use of `reinterpret_cast` I remember was casting to a pointer to the first data member. The other access was through a union where the inactive member shared a common header with the active member. I didn't see anything about accessing a base object through a derived pointer, but maybe that was in the second half? – Kevin Jan 07 '22 at 13:37
  • @Kevin well, at least it is implied: If all POD requirements are fullfilled for both `Foo` and `Bar`, i.e. they are both binary equal to the same POD struct (which is `Foo` itself in this case), it seems legal to `reinterpret_cast` it between the three (two) types. And you could make a `union {Foo f; Bar b}`, initialize either, and becasue they have a common POD layout, it's legal to access data via members of the other. So maybe they don't give this example directly, but again: it seems to be implied. – JHBonarius Jan 07 '22 at 14:26
  • 1
    @JHBonarius I think the fact they had to go through a union implied that just doing a plain `reinterpret_cast` was illegal. But regardless, this isn't useful for the OP because the types their dealing with aren't PODs. – Kevin Jan 07 '22 at 14:30

0 Answers0