10

Say we have this code

class A {
public:
    A() : x(1) {}
    virtual ~A() {}

    int x;
};

class B {
public:
    B() : y(2) {}
    virtual ~B() {}

    void g()
    {
        cout << "B::" << y << endl;
    }

    int y;
};

class C : private A, private B {
public:
    void f()
    {
        B* p = static_cast<B*>( this );
        p->g();
    }
};

int main()
{
    C c;
    ((B*)&c)->g();

    return 0;
}

The C style cast in the main function cannot be correctly expressed in terms of the C++ casts (static_cast, dynamic_cast, reinterpret_cast). But what is the reason to allow this in the first place? Doesn't it hurt encapsulation?

UPDATE This is not a duplicate of the linked question, because this question is about design decisions in C++. It does not ask what I can or cannot do with the language, it asks why certain decisions might have been made.

unkulunkulu
  • 11,576
  • 2
  • 31
  • 49
  • 5
    Because C-style casts are too powerful and should never be used, that's why. – Cat Plus Plus May 26 '12 at 21:52
  • @CatPlusPlus, that's exactly my point, why are they given so much power? – unkulunkulu May 26 '12 at 21:54
  • @unkulunkulu because this is C++. – Seth Carnegie May 26 '12 at 21:54
  • 1
    Because C. They're part of the worst part of the language. – Cat Plus Plus May 26 '12 at 21:55
  • Linked this question to one with high quality answers. – M.M Mar 31 '16 at 08:42
  • @M.M come on, the linked question is "whether one can use C-style cast", my question is about desing and evolution and why a decision was made to allow it. – unkulunkulu Apr 01 '16 at 10:05
  • @unkulunkulu none of the answers here explain why (and the accepted answer is wrong), whereas on the linked question, an answer with a valid use case is given – M.M Apr 01 '16 at 21:26
  • @M.M you could provide a better answer which I will accept, but this is not a duplicate of that question. Also I don't see how it's 'wrong'. Also the linked question deals with implicit cast from within the class, not "C-style cast". – unkulunkulu Apr 03 '16 at 09:26
  • Huh? The code in the linked question is a C-style cast and it says C-style cast in the title. It has good answers. – M.M Apr 03 '16 at 09:34
  • I offer the votes as evidence about the quality of your answer. Not commenting more now - start a meta thread if you feel there is a problem – M.M Apr 03 '16 at 09:34
  • 7 votes is not significant, the question just didn't have any real attention. I won't bother with a meta thread that's for sure. – unkulunkulu Apr 03 '16 at 10:35

3 Answers3

8

When a C-style pointer cast is used between pointers to a base and derived class, it behaves like a static_cast - even if the base is private.

(C-style casts between unrelated pointer types are reinterpret_casts).

The Standard says:

The conversions performed by

— a const_cast (5.2.11),

— a static_cast (5.2.9),

— a static_cast followed by a const_cast,

— a reinterpret_cast (5.2.10), or

— a reinterpret_cast followed by a const_cast,

can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply, with the exception that in performing a static_cast in the following situations the conversion is valid even if the base class is inaccessible:

— a pointer to an object of derived class type or an lvalue or rvalue of derived class type may be explicitly converted to a pointer or reference to an unambiguous base class type, respectively;

— a pointer to member of derived class type may be explicitly converted to a pointer to member of an unambiguous non-virtual base class type;

— a pointer to an object of an unambiguous non-virtual base class type, a glvalue of an unambiguous non-virtual base class type, or a pointer to member of an unambiguous non-virtual base class type may be explicitly converted to a pointer, a reference, or a pointer to member of a derived class type, respectively.

Your situation is described in the first point, so the conversion is done by static_cast and the pointer is adjusted.

Community
  • 1
  • 1
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Note that a `reinterpret_cast` to a private base class is not allowed. – K-ballo May 26 '12 at 21:53
  • `reinterpret_cast` has limits. – Cat Plus Plus May 26 '12 at 21:53
  • @Seth Carnegie yes. You can only do this with C-style casts. See http://stackoverflow.com/a/332086/1203803. –  May 26 '12 at 21:53
  • @Radek'daknok'Slupik is C-style cast not described in terms of the other `_cast` operators? – Seth Carnegie May 26 '12 at 21:54
  • @Seth Carnegie it is, but not exclusively. It can do what all `_cast` operators can do (except for `dynamic_cast`) plus more. –  May 26 '12 at 21:54
  • @Radek'daknok'Slupik is this version of g++ non-conformant in this area then? http://ideone.com/rqKqM – Seth Carnegie May 26 '12 at 21:55
  • @CatPlusPlus same question as above :) – Seth Carnegie May 26 '12 at 21:56
  • @Seth Carnegie hmm, clang compiles it with `-pedantic` and doesn't give warnings. Have to look this up in the standard. –  May 26 '12 at 21:57
  • of course `reinterpret_cast` allows you to convert any pointer to any pointer, it just gives wrong results with classes, in my example it would output `B::1` that's the thing, it doesn't modify pointer values while converting. – unkulunkulu May 26 '12 at 21:59
  • @unkulunkulu and so why the downvote? And if you knew that, why did you ask the question? – Seth Carnegie May 26 '12 at 22:00
  • @SethCarnegie, downvote for messing up the facts instead of answering my question, `reinterpret_cast` is not the same as the C-style cast, it has different result. I didn't know the answer. Moreover, it's not discouraged to answer own questions, I realized the possible answer only after posting and reading some answers and comments. – unkulunkulu May 26 '12 at 22:02
  • @unkulunkulu what facts did I mess up? – Seth Carnegie May 26 '12 at 22:03
  • *A pointer to an object can be explicitly converted to a pointer to a different object type,* says the standard (§5.2.10 expr.reinterpret.cast). The only exception is constness. So yes, you can break private inheritance with `reinterpret_cast`. –  May 26 '12 at 22:03
  • @SethCarnegie, it lets me convert to a private base class, it does pointer modification, it's what my question is all about, it has different effect than that of a `reinterpret_cast` and that's standard. – unkulunkulu May 26 '12 at 22:05
  • @unkulunkulu I never said anything that was incorrect so I messed up no facts, I just misunderstood the question. As for why it modifies the pointer, it's because of what I will add to my answer now.\ – Seth Carnegie May 26 '12 at 22:12
  • @SethCarnegie, that's the interesting piece of information! thanks! – unkulunkulu May 26 '12 at 22:16
  • @unkulunkulu by the way, that quote is from the C++11 standard, although I fully expect the C++03 standard would be almost exactly the same (except for the mention of glvalues, etc) – Seth Carnegie May 26 '12 at 22:19
  • @SethCarnegie, but again, my question was why, I know it's standard, I just wanted to feel the motivation. I still think that my answer contains the possible reason. But maybe this question is a bit subjective then. – unkulunkulu May 26 '12 at 22:19
  • @unkulunkulu Ah, I'm not sure what the motivation behind it is. Probably just because again, if you're explicit about it, then you're trying to tell the compiler to do whatever it can to let you do it. But that's just a guess. Sorry I can't help more. – Seth Carnegie May 26 '12 at 22:21
  • @Radek'daknok'Slupik: You are missing an important fact in your claim of reinterpret cast being able to convert to a private base: it will not do the right thing. Reinterpret will just let you use the *unmodified* pointer as if it was a pointer to the base, but the C style cast fixes the pointers to refer to the appropriate object. You can print the address of each object in the question's code, and then print what `reinterpret_cast(&c)` is compared to `(B*)&c`, and you will see that the two pointers point to different addresses in memory (the offset being the `A` sub object in `C`) – David Rodríguez - dribeas May 26 '12 at 22:22
  • Does the downvoter care to comment? – Seth Carnegie May 26 '12 at 23:14
  • 1
    I'm not the downvoter. However, this answer basically says "It doesn't work as you think it does". But then it corrects itself by adding "Oh, it actually does work that way...". And then it proceeds to basically repeat what id already stated in the question. I don't see the answer to the "why?" question here. – AnT stands with Russia Nov 18 '14 at 00:17
1

It's because in C it was allowed to convert any pointer to any other pointer using this cast and C++ tries to be C-compatible as much as possible, but tries to do a good job to be correct when it comes to classes, so C style cast is stronger than reinterpret_cast in this situation.

unkulunkulu
  • 11,576
  • 2
  • 31
  • 49
-1

A C-style cast allows you to convert any type to any other type. You can do (std::istream*)&c if you'd like, but it's not recommended.

robert
  • 33,242
  • 8
  • 53
  • 74
  • So what can you recommend instead of C style cast in the example in the question? – unkulunkulu May 26 '12 at 22:03
  • You're not supposed to be able to convert one type into a type it privately inherits from. You should use public inheritance if you want to do that. – robert May 26 '12 at 22:05
  • I understand that, hence the question, it's about the design decisions in C++, not about the good code advices :) So I think my answer is to the point. – unkulunkulu May 26 '12 at 22:09
  • @unkulunkulu: The decision is that this is the *lesser* evil. The problem is that C-style casts can do one of many things, and it is not clear from the context of the call. In this particular case if this rule was not in the language it would fall back to a `reinterpret_cast` that is worse than breaking the access specifiers. – David Rodríguez - dribeas May 26 '12 at 22:18
  • This answer is misleading at best - C-style casts behave differently when the two pointers types are related by inheritance. – M.M Mar 31 '16 at 08:40