2

Suppose I have a base struct FOO which is essentially a C-style struct:

struct FOO
{
    double bar1;
    int bar2;
};

And a C++ style struct (which has member functions, no member data, but no v-table):

struct bar : public FOO
{
    double getBar1() const;
    int getBar2() const;
};

I then have a pointer FOO* f, which is actually a FOO: it was created using FOO* f = new FOO(); Is the C-style cast bar* b = (bar*)(f) defined?

Apologies: bar does not contain any data members

P45 Imminent
  • 8,319
  • 4
  • 35
  • 78
  • 1
    The fact that `FOO` is a POD affects initialization in some cases. Inheritance and casting works the same way as any other class/struct – Manos Nikolaidis Oct 14 '15 at 10:59
  • 2
    It is defined and works as static_cast(f). See http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast – Valeri Atamaniouk Oct 14 '15 at 10:59
  • 1
    I don't like the duplicate since I make it very clear that the `struct`s are **not** polymorphic in my case. – P45 Imminent Oct 14 '15 at 11:03
  • 2
    @P45Imminent I’m not sure what *you* mean by “polymorphic” but as far as C++ is concerned, your struct is perfectly fit for polymorphic use — you don’t need a vtable for that. – Konrad Rudolph Oct 14 '15 at 11:09
  • 2
    Polymorphism has nothing to do with this (unless you use `dynamic_cast`). You might want to tell us if `f` actually points to a `FOO` or a `bar`. That affects the way you can use the `b`. – Bo Persson Oct 14 '15 at 11:27
  • 1
    Sorry, I've clarified. `f` has been created using `new FOO()`. Also, by non-polymorphic I mean that you can't use `dynamic_cast` and there is no v-table (I believe the standard guarantees that will be no v-table). – P45 Imminent Oct 14 '15 at 12:02
  • 1
    The standard does not have a notion of vTable, but I'd be surprised if any compiler generated one indeed. – Quentin Oct 14 '15 at 12:05
  • Sorry, what I meant to say is that I think the standard guarantees that the address of the first member is the same as the address of the struct for a non-polymorphic type. – P45 Imminent Oct 14 '15 at 12:09

2 Answers2

3

Is the C-style cast bar* b = (bar*)(f) defined?

C-Style casts will always work, that's why they are the worst among all the casts. For non-polymorphic classes, you may want to use static_cast.

However, in your case you are going towards undefined behavior.

bar* b = (bar*)(f);  // = static_cast<bar*>(f)

The type of f is class FOO (i.e. as in question FOO* f = new FOO()), not class bar. Hence you should not assign such pointers. Even if you assign, you must not use them as of they are of class bar.

P.S. IMO, safest to unsafe casts: dynamic_cast, const_cast, static_cast, reinterpret_cast, C-style cast.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • I'd put `const_cast` as less safe than `static_cast`, as casting away `const`ness is UB if the original object is `const`. In many circumstances a compiler cannot warn you of that. It can *always* warn you about an improper `static_cast`. – Bathsheba Oct 14 '15 at 12:15
  • @Bathsheba, I get what you are saying. For me `const_cast` and `static_cast` are usually in same category safety-wise. But `const_cast` has slightly upper hand because of its less vulnerability. It just deals with `const`s and that's too pointer or reference. But `static_cast` has a wider usage. It can happen between 2 classes belonging to inheritance hierarchy, `void*` to other pointers, as well as 2 integral types like `long` and `short`. I don't think all of these have any compiler warning. So mantra is *less exploitation = more safety*. – iammilind Oct 14 '15 at 12:20
1

I agree with the answer by iammilind - your casting is the same as static_cast, and it's not defined.

The Standard (the linked document is actually a draft, but it doesn't matter for most purposes) says "the result of the cast is undefined" in section 5.2.9, which describes static_cast.

However, since your struct bar has no data members, it's a standard-layout class, and it's layout-compatible with struct FOO. So you can cast your pointer to void* and then to foo*:

bar* b = (bar*)(void*)f

This is better represented in c++ by reinterpret_cast:

bar* b = reinterpret_cast<bar*>(f)

This is described in section 5.2.10:

An object pointer can be explicitly converted to an object pointer of a different type ... the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types ...

Community
  • 1
  • 1
anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • Thank you very much and sorry for invalidating your last draft. Can I cast straight to (bar*) or is it necessary to write (bar*)(void*)? – P45 Imminent Oct 14 '15 at 14:21
  • 1
    @P45Imminent My answer answers specifically that question - "Can I cast straight to (bar*) or is it necessary to write (bar*)(void*)?" - if you need a one-word answer, then **NO**. But there are some fine points. Please read both answers you got - my answer doesn't duplicate the useful info that the other answer has. – anatolyg Oct 14 '15 at 14:27