2

I was reading about access specifiers when applying inheritance, and I know that in private inheritance we could not cast from a derived to a base class using pointers/references.

But when I used reinterpret_cast it worked. below is my test code:

class base {
int _a;
public: 
    base(int a): _a(a) {}
    base(): _a(0) {}
};

class derived : private base
{
public:
    derived(int b):base(b) {};  
};

int main() {

    derived b(25); 
    base &a = static_cast<base&>(b);//this line will generate a compile error
    base &c = reinterpret_cast<base&>(b);  //here it works 
}

So my question is even doing private inheritance, why the base class would be exposed using retinterpret_cast ?

Thank you!

//EDIT 2

class base {
    int _a; 
public:         
    base(int a): _a(a) {}
    base(): _a(100) {}  
    ~base() { std::cout << "deleting base" << _a << "\n"; }
};

class derived : private base
{
public:
    virtual ~derived() = default;
    derived(int b):base(b) {};
};

int main() {

    derived b(25); 
    base &c = reinterpret_cast<base&>(b); 
}

//OutPut : Deleting 25
Blood-HaZaRd
  • 2,049
  • 2
  • 20
  • 43
  • 2
    You shouldn't be using ``static_cast`` or ``reinterpret_cast`` to do upcasting or downcasting. Use ``dynamic_cast`` instead – Asesh Jul 03 '18 at 10:15
  • yeah you are right I have to use dynamic_cst which needs polymorphic class but it was for a test :) – Blood-HaZaRd Jul 03 '18 at 10:16
  • 6
    `reinterpret_cast` is very similar to old C-style casts, in that it basically tells the compiler "I know what I'm doing, don't bother me". If you actually don't know what you're doing, or have made any mistakes, the compiler still won't bother you and will happily lend you a gun so you can shoot yourself in the foot. – Some programmer dude Jul 03 '18 at 10:19
  • In practice and in the simple inheritance case, both `static_cast`, C-style casts, `reinterpret_cast` are compiled as a no-op (that is no code generated, like for the identity function). However, `dynamic_cast` has a runtime cost (and requires some generated code) – Basile Starynkevitch Jul 03 '18 at 10:20
  • @Someprogrammerdude : aware about the evil that lives inside the reinterpret_cast, but does that make the private inheritance violated ? – Blood-HaZaRd Jul 03 '18 at 10:31
  • 3
    "So my question is even doing private inheritance, why the base class would be exposed using retinterpret_cast" - it's not. The `reinterpret_cast` still compiles even if `derived` doesn't inherit from `base` _at all_. – davmac Jul 03 '18 at 10:47

2 Answers2

3

Is private inheritance violated? Not really.

Accessibility in C++ only affects in what scopes can an identifier be used to refer to something in a valid fashion. The system is designed to protect against Murphy, not a Machiavellian trick like you use.

reinterpret_cast is basically you telling the compiler "forget what you know, trust my judgment instead". So it does. You claim this lvalue does in fact refer to a base? Fine, have it your way. But the compiler isn't gonna do anything to protect you, it assumes you know what you are doing. It can break quite easily. There's @Dani's example, and there's this one:

class derived : private base
{
public:
    virtual ~derived() = default;
    derived(int b):base(b) {};  
};

What do you think will happen if you try to use c and call a member function that uses _a? What will it find instead?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Class C derives publicly from A and B (but those latters drove privatly from base if I understood the example) so C wouldn't be able to call _a. Correct me if I am wrong. – Blood-HaZaRd Jul 03 '18 at 10:48
  • @Blood-HaZaRd - What class `C`? The `c` in your example is a reference to a base. – StoryTeller - Unslander Monica Jul 03 '18 at 10:49
  • I thought you pointed to Dani's example in which he used c. So If I try to call a memeber fct using `_a` let say the fct is defined in class base, it shows the value of `_a` – Blood-HaZaRd Jul 03 '18 at 10:58
  • @Blood-HaZaRd - I intentionally added a virutal function. It does not see `_a` http://coliru.stacked-crooked.com/a/dca89f30018ea13c – StoryTeller - Unslander Monica Jul 03 '18 at 11:00
  • @StoryTeller- Sorry did not get you. Could you explain a little further please. the derived class could'nt see `_a` as the inheritance is private !?! – Blood-HaZaRd Jul 03 '18 at 11:21
  • @Blood-HaZaRd - No. You have a derived object. You lie to the compilers it's actually a `base` (and it isn't). The compiler takes you at your word. It doesn't do any adjustment to *really* obtain a base reference (as it would for a public base), so you get undefined behavior. – StoryTeller - Unslander Monica Jul 03 '18 at 11:25
  • Please could you try to Have a look at `EDIT2`, I may did not understood the code you pointing at, but using Edit 2, it seems that `_a` is well printed. May be if I add a memeber variable in derived class of type string for example then _a will have garbage ! is that what you are ointing at ? – Blood-HaZaRd Jul 03 '18 at 11:37
  • 1
    @Blood-HaZaRd - Your edit doesn't use the reference `c` to access the object... The output you see is from regular program flow, when a `derived` object is destroyed, the base class constructors are called as well. I'm sorry, but at this point I think you need to get [back to basics](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – StoryTeller - Unslander Monica Jul 03 '18 at 11:45
  • the uddefined Behaviour is due to how the management of vtable pointer (offset) right ? – Blood-HaZaRd Jul 03 '18 at 13:01
  • @Blood-HaZaRd - Semi formally, yes. – StoryTeller - Unslander Monica Jul 03 '18 at 13:07
  • Ahh what are the keywords that may help ti fugure "the complete formal answer". Iwon't be able to sleep if I coudn't figure the answer :) – Blood-HaZaRd Jul 03 '18 at 13:17
  • @Blood-HaZaRd - The complete formal answer is that it's undefined behavior. The semi formal part is that it doesn't work because compilers put stuff before the members. But the reason compilers *may* put stuff there, is because the standard says "it's undefined behavior". A well-formed C++ program doesn't have undefined behavior. Compiler authors leverage that to do stuff, because a "good" program won't do anything naughty. Sounds circular, I know. But it isn't when one delves into what undefined behavior formally means. I guess you can research "Undefined behavior" in general :) – StoryTeller - Unslander Monica Jul 03 '18 at 13:20
  • @Blood-HaZaRd - If you want to see exactly how much compiler writers can do based on undefined behavior, take a look at this program http://coliru.stacked-crooked.com/a/1ac4fc3ea6a61b85 - Weird that it's calling `print`, right? If you uncomment the `static` it crashes. The reason is explained by what compilers allow themselves to do, based on undefined behavior. – StoryTeller - Unslander Monica Jul 03 '18 at 13:26
  • Nice so It is all related to compiler inner actions that it do. And that is why reinterpret is evil even if we could do the math of pointer offset correction ourself Thank you. – Blood-HaZaRd Jul 03 '18 at 13:35
  • 1
    @Blood-HaZaRd - Almost. Life (and C++) is of course more complicated. Sometimes `reinterpret_cast` can be made to work. The point is that when you `reinterpret_cast`, it tells the compiler you know exactly what's happening. So it's best to be careful. – StoryTeller - Unslander Monica Jul 03 '18 at 13:36
  • I even see in some code sthing like `reinterpret_cast(static_cast(&b) )` a mix of static and reinterpret but here we are 100% sure on what we manipulate as static retrieve the good thing – Blood-HaZaRd Jul 03 '18 at 13:39
  • 1
    @Blood-HaZaRd - Yup, it's one of those places where deeper knowledge is required. The static cast asks the compiler to adjust the pointer correcly, and the reinterpret_cast then takes that returned pointer and makes an integer out of it. The result is highly implementation defined. – StoryTeller - Unslander Monica Jul 03 '18 at 13:43
1

reinterpret_cast is not the same as static_cast
Consider the following example:

class A { int a; }
class B { int b; }
class C : public A, B { }

Casting C to B using static_cast would change the pointer to the correct result, while reinterpret_cast would keep the pointer the same which is not correct.

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • `static_cast` won't work outside the scope of `C` here. – StoryTeller - Unslander Monica Jul 03 '18 at 10:26
  • 1
    It is important if you assert something to be true. *"Casting C to B using static_cast would change the pointer to the correct result"* - Not outside the scope of `C`. It's important because someone in the future is likely going to try it, but it won't compile. `static_cast` respects access modifiers. And `B` is still private, btw. – StoryTeller - Unslander Monica Jul 03 '18 at 10:30
  • I'm not trying to say your entire argument is wrong, MI is indeed an example for when it can fail spectacularly. – StoryTeller - Unslander Monica Jul 03 '18 at 10:31
  • @Dani : my aim is to know why when I use reinterpret_cast the private inheritance it could retrieve a ref to the base class ? sorry if I did not see the clue in your answer. – Blood-HaZaRd Jul 03 '18 at 10:35
  • 1
    @Blood-HaZaRd: it’s undefined behavior and in the common compilers it works in some conditions. In multiple inheritance like in my example it wouldn’t work. In your case it probably worked since the base class occupies offset 0 in the class. – Daniel Jul 04 '18 at 11:49
  • @Dani: yeah you pointed right. Managing offset by myOwn is totally Evil !! – Blood-HaZaRd Jul 04 '18 at 11:55