Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

reinterpret_cast vs. static_cast

3 views
Skip to first unread message

James Kanze

unread,
Feb 10, 1997, 3:00:00 AM2/10/97
to

Stephen...@eng.sun.com (Steve Clamage) writes:

|> In article 1453...@library.airnews.net, rmas...@r2m.com (Robert
|> Mashlan) writes:
|> >
|> >Assuming that type T2 has stricter or equal alignment restrictions
|> >than type T1, Is it possible that the following two expressions
|> >return different results?
|> >
|> > T2* pt2;
|> >
|> > T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
|> >
|> >vs.
|> >
|> > T1* pt1 = reinterpret_cast<T1*>(pt2);
|>
|> It is always possible for static_cast and reinterpret_cast to produce
|> different results, because the results of a reinterpret_cast are
|> implementation-defined.
|>
|> The example static_cast above is invalid if T1 and T2 are unrelated
|> types, which they appear to be. The compiler must issue a diagnostic.

Something I don't understand: how does the fact that T1 and T2 are or
are not related affect the static_cast's in the example? There is no
direct static_cast between the two classes, only to and from void*.

(This leads to a further question: is a static_cast FROM void* legal?
Should it be?)

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++...@ncar.ucar.edu ]

Steve Clamage

unread,
Feb 10, 1997, 3:00:00 AM2/10/97
to

In article 2111...@library.airnews.net, rmas...@r2m.com (Robert Mashlan) writes:
>Stephen...@eng.sun.com (Steve Clamage) wrote:
>
>>For unrelated pointers, assuming it makes sense to perform the
>>conversion at all, you have no choice but to use reinterpret_cast.
>
>The two types are unrelated. Should this work on an implementation
>where char * uses a different representation than a pointer to a
>class?

As I said in my previous post, the effect of reinterpret_cast is
implementation-defined. You can't predict what the effect will be
without reading the documentation for the system.

A note in the draft standard says the effect of a pointer cast is
"intended to be unsurprising to those who know the addressing structure
of the underlying machine."

Arbitrary fiddling with pointers is never going to be portable.
The reinterpret_cast is intended to allow "type punning" in those
cases where it makes sense. That varies case by case, system by
system. In the end, it is up to the C++ implementor to decide.

I'm not trying to make this sound difficult. If you work on a
system where pointers are all the same size and have the same
representation, casting to another pointer type should work the
way you expect (as long as you don't violate alignment restrictions).

But if you are asking if code like that is portable, the answer
has to be "no".
---
Steve Clamage, stephen...@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]

Steve Clamage

unread,
Feb 10, 1997, 3:00:00 AM2/10/97
to

In article 69...@pratique.fr, Valentin Bonnard <bonn...@pratique.fr> writes:

>Steve Clamage wrote:
>>
>> The example static_cast above is invalid if T1 and T2 are unrelated
>> types, which they appear to be. The compiler must issue a diagnostic.
>
>Are you sure ? (I mean after re-reading the original post.)
>
>Can you justify this (DWP citation) ?

Section 5.2.9 "Static cast" of the draft is too lengthy to reproduce here.
It lists the casts which are allowed, and says, "No other conversion shall
be performed explicitly using a static_cast." The "shall" means that
a diagnostic is required if the condition is violated. Pointers to
unrelated types (not part of the same heirarchy) are not in the list
of allowable casts.

>> For unrelated pointers, assuming it makes sense to perform the
>> conversion at all, you have no choice but to use reinterpret_cast.
>

>Right, but that was not the original poster's question.

The original question asked if a particluar static_cast and reinterpret
cast could yield different results. The static_cast isn't allowed, and
IMHO that is part of the answer.

Steve Clamage

unread,
Feb 11, 1997, 3:00:00 AM2/11/97
to

In article f...@vx.cit.alcatel.fr, James Kanze <james-alb...@vx.cit.alcatel.fr> writes:
>Stephen...@eng.sun.com (Steve Clamage) writes:
>
>|> In article 1453...@library.airnews.net, rmas...@r2m.com (Robert
>|> Mashlan) writes:
>|> >
>|> >Assuming that type T2 has stricter or equal alignment restrictions
>|> >than type T1, Is it possible that the following two expressions
>|> >return different results?
>|> >
>|> > T2* pt2;
>|> >
>|> > T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
>|> >
>|> >vs.
>|> >
>|> > T1* pt1 = reinterpret_cast<T1*>(pt2);
>|>
>|> It is always possible for static_cast and reinterpret_cast to produce
>|> different results, because the results of a reinterpret_cast are
>|> implementation-defined.
>|>
>|> The example static_cast above is invalid if T1 and T2 are unrelated
>|> types, which they appear to be. The compiler must issue a diagnostic.
>
>Something I don't understand: how does the fact that T1 and T2 are or
>are not related affect the static_cast's in the example? There is no
>direct static_cast between the two classes, only to and from void*.

Sorry, I was compressing two things into one sentence. Here's an
expanded version.

In the submitted example, the cast to void* loses type information that
might be important. The casts might do the right thing, but you can't
depend on it. (For example, if T1 and T2 are part of the same hierarchy,
the T1 and T2 sub-objects have the same address, and the pointers have
the same representation, the cast will work. It will probably also work
for unrelated pointer types which have the same representation, given
the alignment preconditions.)

Contrarywise, a cast like
static_cast<T1*>(pt2)
might be valid if T1 and T2 are part of the same hierarchy, but
not otherwise. (It is valid if T1 is a base class of T2, or if
T2 is a non-virtual base class of T1.)

>(This leads to a further question: is a static_cast FROM void* legal?
>Should it be?)

Yes, since a static cast can reverse most implicit conversions. (I think
the only exception is reversing a conversion to a virtual base class.)
In particular, you can static_cast any data pointer to void* and back to
the original type and get a pointer that compares equal to the original.

---
Steve Clamage, stephen...@eng.sun.com

Fergus Henderson

unread,
Feb 11, 1997, 3:00:00 AM2/11/97
to

James Kanze <james-alb...@vx.cit.alcatel.fr> writes:

>Stephen...@eng.sun.com (Steve Clamage) writes:


>
>|> rmas...@r2m.com (Robert Mashlan) writes:
>|> >
>|> > T2* pt2;
>|> > T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
>|>

>|> The example static_cast above is invalid if T1 and T2 are unrelated
>|> types, which they appear to be. The compiler must issue a diagnostic.
>
>Something I don't understand: how does the fact that T1 and T2 are or
>are not related affect the static_cast's in the example? There is no
>direct static_cast between the two classes, only to and from void*.

No, a direct static_cast between a pointer to a derived class and
a pointer to its base class is fine (in either direction).
See [expr.static.cast] paragraphs 6-8.

>(This leads to a further question: is a static_cast FROM void* legal?

Yes, it is well-formed, since that's the inverse of a standard conversion.

>Should it be?)

Yes.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

Robert Mashlan

unread,
Feb 11, 1997, 3:00:00 AM2/11/97
to

Stephen...@Eng.Sun.COM (Steve Clamage) wrote:

>I'm not trying to make this sound difficult. If you work on a
>system where pointers are all the same size and have the same
>representation, casting to another pointer type should work the
>way you expect (as long as you don't violate alignment restrictions).
>
>But if you are asking if code like that is portable, the answer
>has to be "no".

I'm still not clear on this.

One of the things that is confusing me, is that in C, you can write:

typedef struct { ... } T;

T* p = (T* )malloc( sizeof(T) );

malloc is supposed to return a void* that is of suitable alignment for
any data object, and I had thought this is portable code.

I think you said in another post that you can't use static_cast to
cast a void * to a T*, so this would imply that the C style cast
above is a reinterpret_cast. Since you say the results of using
reinterpret_cast are non-portable, that means the code above would be
non-portable in C++. Is this conclusion correct?

---
Robert Mashlan R2M Software rmas...@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/

Steve Clamage

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to

In article 2308...@library.airnews.net, rmas...@r2m.com (Robert

Mashlan) writes:
>Stephen...@Eng.Sun.COM (Steve Clamage) wrote:
>
>>I'm not trying to make this sound difficult. If you work on a
>>system where pointers are all the same size and have the same
>>representation, casting to another pointer type should work the
>>way you expect (as long as you don't violate alignment restrictions).
>>
>>But if you are asking if code like that is portable, the answer
>>has to be "no".
>
>I'm still not clear on this.
>
>One of the things that is confusing me, is that in C, you can write:
>
> typedef struct { ... } T;
>
> T* p = (T* )malloc( sizeof(T) );
>
>malloc is supposed to return a void* that is of suitable alignment for
>any data object, and I had thought this is portable code.
>
>I think you said in another post that you can't use static_cast to
>cast a void * to a T*,

I hope I didn't say that, because it isn't true. You can use a
static_cast to cast from void* to any data pointer type, but the
usefulness of the result depends on several factors. The cast
itself is allowed.

> so this would imply that the C style cast
>above is a reinterpret_cast. Since you say the results of using
>reinterpret_cast are non-portable, that means the code above would be
>non-portable in C++. Is this conclusion correct?

I was speaking always of the general case. Given pointers to unrelated
types T1 and T2, converting a T2* to a T1* is in general not portable
in C or in C++. (And that was the original question that started this
thread.) In C++ the conversion can be performed in general only
with a reinterpret_cast. The conversion semantics of a reinterpret_cast
are defined by the implementation, and so will be in general non-portable.
(Suppose the pointers are different sizes, and you convert a big
pointer to a little pointer. The standard can't make any promises
about the result.)

Malloc is not a general case, but a specific case. Type void* is also
a special case. Type void* is required to have the characteristic that
any data pointer can be converted to void* without loss of information.
The requirements on malloc include that it return a pointer to a block
of memory suitably aligned for any object of the requested size, and
that the void* returned be convertible to a pointer to any such data
object.

Your malloc example is correct and portable code, because of the
special requirements on malloc and void*. The conversion is
performed via a static_cast.

---
Steve Clamage, stephen...@eng.sun.com
---

Robert Mashlan

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to

Stephen...@eng.sun.com (Steve Clamage) wrote:

>I was speaking always of the general case. Given pointers to unrelated
>types T1 and T2, converting a T2* to a T1* is in general not portable
>in C or in C++. (And that was the original question that started this
>thread.) In C++ the conversion can be performed in general only
>with a reinterpret_cast. The conversion semantics of a reinterpret_cast
>are defined by the implementation, and so will be in general non-portable.
>(Suppose the pointers are different sizes, and you convert a big
>pointer to a little pointer. The standard can't make any promises
>about the result.)
>
>Malloc is not a general case, but a specific case. Type void* is also
>a special case. Type void* is required to have the characteristic that
>any data pointer can be converted to void* without loss of information.
>The requirements on malloc include that it return a pointer to a block
>of memory suitably aligned for any object of the requested size, and
>that the void* returned be convertible to a pointer to any such data
>object.
>
>Your malloc example is correct and portable code, because of the
>special requirements on malloc and void*. The conversion is
>performed via a static_cast.

Hello Steve,

Ok, that makes sense. Now consider the following code:

struct T { unsigned refcnt; } ;

T* CreateT( char *text )
{
T* tp static_cast<T*>( malloc( sizeof(T) + strlen(text) + 1 );

char* cp = static_cast<char*>( static_cast<void*>( tp+1) );
strcpy(cp,text);
return tp;
}

In what way could this code not be portable? The potential
problems, as I see it are:

1) The requested size given to malloc could cause a problem with
producing a pointer of suitable alignment for T. If this is the case,
the requested size could be rounded up such that size % sizeof(T) ==
0, which would make the memory block of suitable alignment for an
array of T. (Is it conceivable for an implementation to require this?)

2) There is some completely oddball implementation that needs char* to
be more strictly aligned than T*. I would think this possibility is
not worth worrying about.

If you make the leap of faith that 2 is not going to be a problem,
should I stick with using the double static_cast to convert the T* to
char*, or would reinterpret_cast do the same job, to have the example
be portable?

A related question -- in terms of new style casts, what does the old
style cast cp = (char *)tp do? Is this a double static_cast or a
single reinterpret_cast, if in fact the two are different?

rm

---
Robert Mashlan R2M Software rmas...@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/

---

James Kanze

unread,
Feb 13, 1997, 3:00:00 AM2/13/97
to

rmas...@r2m.com (Robert Mashlan) writes:

No. The static_cast< T* > could add tagging information, which would be
maintained through further static_cast's.

Presumably, if you rewrote the line with the two static_cast's:

char* cp = reinterpret_cast< char* >( tp + 1 ) ;

the implementation would also have the right to generate a null pointer,
either because the implementation's definition of reinterpret_cast is
to always return a null pointer, or because the implementation checks
the tagging information in reinterpret_cast, and generates a null
pointer in case of mismatch.

Such an implementation may be complying with the letter of the standard,
but it is certainly going against the intent. I actually have code like
the above (with the reinterpret_cast); I consider it "portable enough",
in that I don't think it will fail on any "reasonable" implementation.

|> The potential
|> problems, as I see it are:
|>
|> 1) The requested size given to malloc could cause a problem with
|> producing a pointer of suitable alignment for T. If this is the case,
|> the requested size could be rounded up such that size % sizeof(T) ==
|> 0, which would make the memory block of suitable alignment for an
|> array of T. (Is it conceivable for an implementation to require this?)

malloc must produce a pointer suitably aligned for all types. In all
cases.

|> 2) There is some completely oddball implementation that needs char* to
|> be more strictly aligned than T*. I would think this possibility is
|> not worth worrying about.

Agreed. I seem to recall some sort of discussion in comp.std.c which
implied that the alignment requirement for T couldn't be greater than
"sizeof( T )". (I think it had to do with the fact that legal pointer
arithmetic could result in misaligned pointers otherwise.) Since
sizeof( char ) is by definition 1, and all sizeof must be integral, I
think that you can conclude that an implementation in which char
requires more strict alignment than some type T is not conforming.
(That is to say: there is no such rule directly, but any possible
implementation with this characteristic would fail on some other rule.)

|> If you make the leap of faith that 2 is not going to be a problem,
|> should I stick with using the double static_cast to convert the T* to
|> char*, or would reinterpret_cast do the same job, to have the example
|> be portable?

I'd go with the reinterpret_cast. IMHO, there is no way that is
guaranteed portable, but the reinterpret_cast will work on any
implementation that conforms to the intent of the standard, whereas the
double static_cast could fail on an implementation with tagged pointers.

|> A related question -- in terms of new style casts, what does the old
|> style cast cp = (char *)tp do? Is this a double static_cast or a
|> single reinterpret_cast, if in fact the two are different?

A reinterpret_cast, I think.

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

---

Steve Clamage

unread,
Feb 13, 1997, 3:00:00 AM2/13/97
to

In article 4308...@library.airnews.net, rmas...@r2m.com (Robert Mashlan) writes:
>
>Ok, that makes sense. Now consider the following code:
>
> struct T { unsigned refcnt; } ;
>
> T* CreateT( char *text )
> {
> T* tp = static_cast<T*>( malloc( sizeof(T) + strlen(text) + 1 );

>
> char* cp = static_cast<char*>( static_cast<void*>( tp+1) );
> strcpy(cp,text);
> return tp;
> }
>
>In what way could this code not be portable?

Anyone who wrote code like that who works for me would be shot^H^H^H
encouraged to write something more straightforward and reliable. Who can
understand and maintain code that depends on unnamed and invisible parts
of a struct? How will you ensure that anyone using a T object will never
do anything based on sizeof T? (Those are rhetorical questions. I don't
really want to know.)

Also please note that T is not really a type, since no two instances of
a T have the same size or structure, except accidently. Yet T looks like
a type and the compiler will treat it as a type. Why would this be an
advantage, except for entries in an Obfuscated C++ contest?

I assume you want to avoid for efficiency reasons the obvious and portable
solution of storing a pointer to an allocated object. There are ways
around that problem without resorting to code that is likely to get
you in trouble down the road.

> The potential
>problems, as I see it are:
>
>1) The requested size given to malloc could cause a problem with
>producing a pointer of suitable alignment for T. If this is the case,
>the requested size could be rounded up such that size % sizeof(T) ==
>0, which would make the memory block of suitable alignment for an
>array of T.

That is not allowed to be an issue. When you request N bytes from
malloc, it returns a pointer to a region of memory that contains
at least N bytes, and which is suitably aligned for any object
that can fit in N bytes. That is an absolute requirement on malloc.
(Unless malloc fails because the free store cannot supply a contiguous
region of suitable size.) C and C++ assume that smaller basic types
never have stricter alignment requirements than larger basic types.
(I don't know if that is stated explicitly in the standard, but it
it follows implicitly from various requirements.)

>A related question -- in terms of new style casts, what does the old
>style cast cp = (char *)tp do? Is this a double static_cast or a
>single reinterpret_cast, if in fact the two are different?

It is a reinterpret_cast. The double static_cast (T1 to void* to T2,
where T1 and T2 are unrelated and neither is void*) has no portable
semantics. (Neither does reinterpret_cast, of course.)

As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
double cast is likely to behave as you expect, and is likely to have the
same result as reinterpret_cast. If that happens not to be true on
some platform, you are out of luck. The standard does not promise that
it is so, as far as I can tell.
---
Steve Clamage, stephen...@eng.sun.com
---

Robert Mashlan

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to

Stephen...@Eng.Sun.COM (Steve Clamage) wrote:

>Anyone who wrote code like that who works for me would be shot^H^H^H
>encouraged to write something more straightforward and reliable. Who can
>understand and maintain code that depends on unnamed and invisible parts
>of a struct? How will you ensure that anyone using a T object will never
>do anything based on sizeof T? (Those are rhetorical questions. I don't
>really want to know.)

Steve, even if you don't want to know - I've used them for two
different applications, 1) as a a string reference for a string like
class, and 2) as a header for a memory blocks in a heap. The
resulting class are not be derived from, and in my implementation of
them, they have private constructors and destructors, and object
creation and destruction is handled through other members functions.
In practice, these types of objects are only used privately by some
other object, and are not for public consumption for the rest of the
program.

In any case, what kinds of things are you thinking of that someone
would want to use sizeof(T) with the class? Even with normal
classes, there are very few uses for it.

>Also please note that T is not really a type, since no two instances of
>a T have the same size or structure, except accidently. Yet T looks like
>a type and the compiler will treat it as a type. Why would this be an
>advantage, except for entries in an Obfuscated C++ contest?

>I assume you want to avoid for efficiency reasons the obvious and portable
>solution of storing a pointer to an allocated object. There are ways
>around that problem without resorting to code that is likely to get
>you in trouble down the road.

The reason that I prefer this approach is that it's more object
oriented. I think the alternatives you have in mind don't even use
objects -- such as storing a reference count in the first few chars of
an allocated char array (which would also involve a cast between two
types, unless you're happy with a char sized reference count) The
intent of my approach is to create a class which binds all the data
together -- rather than write a bunch of code not as tightly bound
with the data involved. In my opinion, the alternatives are just
difficult as this approach.

>>A related question -- in terms of new style casts, what does the old
>>style cast cp = (char *)tp do? Is this a double static_cast or a
>>single reinterpret_cast, if in fact the two are different?
>
>It is a reinterpret_cast. The double static_cast (T1 to void* to T2,
>where T1 and T2 are unrelated and neither is void*) has no portable
>semantics. (Neither does reinterpret_cast, of course.)
>
>As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
>double cast is likely to behave as you expect, and is likely to have the
>same result as reinterpret_cast. If that happens not to be true on
>some platform, you are out of luck. The standard does not promise that
>it is so, as far as I can tell.

I'm still not understanding why this would be so -- is there something
magical about the void* returned from malloc, as compared to a void*
(assumed to have the appropriate alignment) that points to a middle of
a malloc'ed block?

Robert


---
Robert Mashlan R2M Software rmas...@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/

---

Valentin Bonnard

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to

Steve Clamage wrote:
> Yes, since a static cast can reverse most implicit conversions. (I think
> the only exception is reversing a conversion to a virtual base class.)

There is annother special case but it's not clearly addressed
in the april 95 DWP (in fact I think it's not addressed at
all :-( ); it's static_cast<T*>(bool (p)).

This seems legal, but its behaviour isn't described in the draft;
does it mean that it's unspecified behaviour ?

Note: g++ allow it and convert true to a pointer with value 1,
like (T*) 1.

--

Valentin Bonnard
mailto:bonn...@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)

Steve Clamage

unread,
Feb 16, 1997, 3:00:00 AM2/16/97
to

In article 253...@library.airnews.net, rmas...@r2m.com (Robert

Mashlan) writes:
>Stephen...@Eng.Sun.COM (Steve Clamage) wrote:
>
>>As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
>>double cast is likely to behave as you expect, and is likely to have the
>>same result as reinterpret_cast. If that happens not to be true on
>>some platform, you are out of luck. The standard does not promise that
>>it is so, as far as I can tell.
>
>I'm still not understanding why this would be so -- is there something
>magical about the void* returned from malloc, as compared to a void*
>(assumed to have the appropriate alignment) that points to a middle of
>a malloc'ed block?

The cast from the void* returned from malloc works (within specified
limits) because a language rule says it must. If you call malloc to get
N bytes, the returned address must be suitably aligned for any data type
of N bytes. If you adjust the pointer to point inside that memory space,
whether it is suitably aligned for your particular purpose depends on the
details of the platform, the implementation, and the particular value of
the pointer.

Many popular platforms feature byte-addressed memory, and pointers which
are simple addresses, all pointers being the same size and having the
same representation. For such a platform, it would be reasonable to
expect the result of any reinterpret_cast, or any static_cast to or
from void*, to leave the bits unchanged. It would also be reasonable to
expect that if you obey alignment constraints, any pointer conversion
will have a simple and predictable result.

Properties like "all pointers look alike", "all pointers are the same size",
"reinterpret_cast doesn't change the bits" are not universal, however,
and the standard doesn't require them to hold.

One of the major reasons for adding the four new cast types to C++ was
to make it easy to find casts in a program, so that each cast could
be inspected when porting code to determine whether it makes unportable
assumptions. All safe and portable type conversions can occur implicitly.
Any conversion requiring a cast is potentially unportable or is otherwise
potentially dangerous.
---
Steve Clamage, stephen...@eng.sun.com

Alexandre Oliva

unread,
Feb 17, 1997, 3:00:00 AM2/17/97
to

Valentin Bonnard writes:

> Steve Clamage wrote:
>> Yes, since a static cast can reverse most implicit conversions. (I think
>> the only exception is reversing a conversion to a virtual base class.)

> There is annother special case but it's not clearly addressed
> in the april 95 DWP (in fact I think it's not addressed at
> all :-( ); it's static_cast<T*>(bool (p)).

The Nov'96 DWP explicitly disallows this in [expr.static.cast]:

6 The inverse of any standard conversion sequence (_conv_), other than
[...] boolean (_conv.bool_) conversions, can be performed explicitly
using static_cast [...]

--
Alexandre Oliva
mailto:ol...@dcc.unicamp.br mailto:aol...@acm.org
Universidade Estadual de Campinas, SP, Brasil

John E. Potter

unread,
Feb 18, 1997, 3:00:00 AM2/18/97
to

Fergus Henderson (f...@murlibobo.cs.mu.OZ.AU) wrote:

: James Kanze <james-alb...@vx.cit.alcatel.fr> writes:
: >Stephen...@eng.sun.com (Steve Clamage) writes:
: >|> rmas...@r2m.com (Robert Mashlan) writes:
: >|> > T2* pt2;
: >|> > T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
: >|> The example static_cast above is invalid if T1 and T2 are unrelated
: >|> types, which they appear to be. The compiler must issue a diagnostic.

: >Something I don't understand: how does the fact that T1 and T2 are or
: >are not related affect the static_cast's in the example? There is no
: >direct static_cast between the two classes, only to and from void*.
: No, a direct static_cast between a pointer to a derived class and
: a pointer to its base class is fine (in either direction).
: See [expr.static.cast] paragraphs 6-8.

See also paragraph 9 which allows the above cast from void* to any T1*.

: >(This leads to a further question: is a static_cast FROM void* legal?


: Yes, it is well-formed, since that's the inverse of a standard conversion.
: >Should it be?)
: Yes.

This has been a very confused thread. I have no problem with any of the
stuff on reinterpret_cast because the rules are clear in the section on
that subject. Reinterpret_cast is implementation defined, yet there are
some things guarantied. If the size and alignment of short are two
and long are four then
reinterpret_cast<long*>(reinterpret_cast<short*>(longPtr))
is a no-op.

[ No, an implementation may not return null or abort ]

And the following is unspecified
reinterpret_cast<short*>(reinterpret_cast<long*>(shortPtr))

I can't find anything that says that
static_cast<short*>(static_cast<void*>(shortPtr))
is a no-op. The inner one is covered by standard conversions and I guess
the outer one is covered by malloc. It then seems to follow that
static_cast<short*>(static_cast<void*>(longPtr))
is a no-op. That seems to say that the answer to the original question
is that they are the same.

But what about
static_cast<long*>(static_cast<void*>(shortPtr))
I can't find it as defined, undefined, unspecified, or implementation
defined. Is it unmentioned or can someone give me a pointer? And what
does unmentioned mean (pointer please)?

And a long unanswered question, "is
static_cast<short*>(longPtr)
well-formed?" The inner cast to void* is a standard conversion. If so,
it seems that (short*)longPtr is a static_cast.

Any enlightenment appreciated,
John

Bill Wade

unread,
Feb 19, 1997, 3:00:00 AM2/19/97
to

Steve Clamage <Stephen...@Eng.Sun.COM> wrote in article

> C and C++ assume that smaller basic types
> never have stricter alignment requirements than larger basic types.
> (I don't know if that is stated explicitly in the standard, but it
> it follows implicitly from various requirements.)

I'd never noticed that. Is there a simple example (in either language)
which shows why my machine can't have
sizeof(int) == 2
sizeof(float) == 3
and require 2 char alignment for ints and no particular alignment (1 char)
for floats?
union{ int a; float b; }
has sizeof == 4, but that seems ok. From the point of the memcpy rules it
seems to have the same semantics as
union{ int a; char b[3]; }
---

J. Kanze

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to

Stephen.Clamage@Eng (Steve Clamage) writes:

|> >(This leads to a further question: is a static_cast FROM void* legal?

|> >Should it be?)


|>
|> Yes, since a static cast can reverse most implicit conversions. (I think
|> the only exception is reversing a conversion to a virtual base class.)

|> In particular, you can static_cast any data pointer to void* and back to
|> the original type and get a pointer that compares equal to the original.

This answers the first question. But should it be. Practically, it
means that static_cast can be used to cast between arbitrary pointer
types, since there is an implicit cast from any pointer type to void*,
which will allow the compiler to invoke the static cast. I don't think
that this is what the committee wanted, and IMHO, it breaks one of the
largest single reasons for the new cast syntax: a "sideways" cast within
a class hierarchy is no longer an error.

On the other hand, if casting from void* requires a reinterpret_cast, we
have (or at least should have) a case where the semantics of
reinterpret_cast are NOT implementation defined.

--
James Kanze +33 (0)1 39 55 85 62 email: ka...@gabi-soft.fr
GABI Software, Sarl., 22 rue Jacques-Lemercier, 78000 Versailles, France


-- Conseils en informatique industrielle --

---

Valentin Bonnard

unread,
Feb 24, 1997, 3:00:00 AM2/24/97
to

John E. Potter wrote:
> This has been a very confused thread.

I agree.

> I have no problem with any of the
> stuff on reinterpret_cast because the rules are clear in the section on
> that subject. Reinterpret_cast is implementation defined, yet there are
> some things guarantied. If the size and alignment of short are two
> and long are four then
> reinterpret_cast<long*>(reinterpret_cast<short*>(longPtr))
> is a no-op.
>
> [ No, an implementation may not return null or abort ]

Why should 'assert (reinterpret_cast<short*>(longPtr))' work ?

With tagged pointer, the actual value could be saved in
the tag and restored after.

> And the following is unspecified
> reinterpret_cast<short*>(reinterpret_cast<long*>(shortPtr))

Right

> I can't find anything that says that
> static_cast<short*>(static_cast<void*>(shortPtr))
> is a no-op. The inner one is covered by standard conversions and I guess
> the outer one is covered by malloc. It then seems to follow that
> static_cast<short*>(static_cast<void*>(longPtr))
> is a no-op. That seems to say that the answer to the original question
> is that they are the same.

IMO void type has been forgotten in the [expr.static.cast]
clause but the intent is quite clear; while well-formed
(no diagnostic needed), it's undefined behaviour to do
that.

> But what about
> static_cast<long*>(static_cast<void*>(shortPtr))
> I can't find it as defined, undefined, unspecified, or implementation
> defined. Is it unmentioned or can someone give me a pointer? And what
> does unmentioned mean (pointer please)?

The same. static_cast should only be used to go back to the
dynamic type of the object (this is != const_cast where you
need not to know the dynamic type [dynamic constness] of
the object).

So while (1) is well-defined, (2) is not in the following:

class Base {};
class Der : public Base {};

Base b;
const Base cb;
Der d;

void* pv; // void is a direct base class for everything
Base* pb;
const Base* pcb;
Der* pd;

pcb = &b;
const_cast<Base*> (pcb); // OK

pcb = &cb;
const_cast<Base*> (pcb); // (1) well-formed, well-defined

pb = &d;
static_cast<Der*> (pb); // OK

pb = &b;
static_cast<Der*> (pb); // (2) well-formed, undefined

pv = &b;
static_cast<Der*> (pv); // (2b) well-formed, undefined

So the programmer must make sure static_cast doesn't go
under it's dynamic type in the class latice but there
aren't any such restrictions on const_cast.

> And a long unanswered question, "is
> static_cast<short*>(longPtr)
> well-formed?" The inner cast to void* is a standard conversion. If so,
> it seems that (short*)longPtr is a static_cast.

Has I replied James, IMO it's not.

I wrote:

>
> I don't think so; the Dec 96 DWP says:
>
> 5.2.9 Static cast [expr.static.cast]
>
> 3 Otherwise, the static_cast shall perform one of the conversions listed
> below. No other conversion shall be performed explicitly using a
> static_cast.
>
> IMO it means that static_cast can't perform std conversions
> unless explicitly stated.
>
> So any* -> void* is a compile-time correct static_cast
> void* -> other* is a compile-time correct static_cast
>
> but any* -> other* is NOT

I'll add that the compilers I use things the same and if
it's not true then the static/reinterpret casts has the
practical value of a simple comment.

--

Valentin Bonnard
mailto:bonn...@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)

Jason Merrill

unread,
Feb 24, 1997, 3:00:00 AM2/24/97
to

>>>>> J Kanze <ka...@gabi-soft.fr> writes:

> This answers the first question. But should it be. Practically, it
> means that static_cast can be used to cast between arbitrary pointer
> types, since there is an implicit cast from any pointer type to void*,
> which will allow the compiler to invoke the static cast.

Except that implicit conversions are not performed on the operand of
static_cast in order to find a type that can be cast from; the new-style
casts only perform a single conversion.

Jason

Steve Clamage

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

In article f...@gabi-soft.fr, ka...@gabi-soft.fr (J. Kanze) writes:
>Stephen.Clamage@Eng (Steve Clamage) writes:
>
>|> >(This leads to a further question: is a static_cast FROM void* legal?
>|> >Should it be?)
>|>
>|> Yes, since a static cast can reverse most implicit conversions. (I think
>|> the only exception is reversing a conversion to a virtual base class.)
>|> In particular, you can static_cast any data pointer to void* and back to
>|> the original type and get a pointer that compares equal to the original.
>
>This answers the first question. But should it be. Practically, it
>means that static_cast can be used to cast between arbitrary pointer
>types, since there is an implicit cast from any pointer type to void*,
>which will allow the compiler to invoke the static cast. I don't think
>that this is what the committee wanted, and IMHO, it breaks one of the
>largest single reasons for the new cast syntax: a "sideways" cast within
>a class hierarchy is no longer an error.

I'm not sure what you are trying to say. You can't use static_cast
to cast directly sideways in a hierarchy. That is, given
class VB { ... };
class A : public virtual VB { ... };
class B : public virtual VB { ... };
class C : public A, public B { ... };
A* pa = new C;
B* pb = static_cast<B*>(pa); // ERROR
An old-style cast here would not result in an error message (it is
treated as a reinterpet_cast under the new rules, and is quietly accepted
under the old rules), but would not give a useful answer. The static_cast
must yield an error. Isn't that what we want?

You can write the "double cast"
B* pb = static_cast<B*>(static_cast<void*>(pa));
which is perhaps your complaint. But programmers must be educated
never to write code like this, just as they must be educated to
avoid old-style casts: you can easily wind up with syntactically
valid code with unknown or even undefined semantics.

Safe casts in C++ may all be implicit. If you write an explicit cast,
it should always be considered a flag that something unportable might be
occuring. Many of the new-style casts are in fact safe if they compile.
A "double cast", it seems to me, must always signal something dangerous
and definitely unportable, even moreso than a reinterpret_cast.

I think the only legitimate use for a "double cast" is when one of
the pair is a const_cast, since the other three casts cannot remove
constness. But removing const is in general dangerous, of course.
You must somehow know that the object referred to is not really const.
---
Steve Clamage, stephen...@eng.sun.com

John E. Potter

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

Jason Merrill (ja...@cygnus.com) wrote:
: >>>>> J Kanze <ka...@gabi-soft.fr> writes:

: > This answers the first question. But should it be. Practically, it


: > means that static_cast can be used to cast between arbitrary pointer
: > types, since there is an implicit cast from any pointer type to void*,
: > which will allow the compiler to invoke the static cast.

: Except that implicit conversions are not performed on the operand of


: static_cast in order to find a type that can be cast from; the new-style
: casts only perform a single conversion.

I like that. But does CD2 say that?

I find in 4/5 that in some cases, standard conversions are suppressed
in some contexts and that exceptions are given in the descriptions.

In 5.2.9/4 exceptions are given for static_cast<void>. It seems that
these exceptions would not be required if all standard conversions
were suppressed.

No idea what the repercussions would be if that change were made.

John
---

James Kanze

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

Stephen...@eng.sun.com (Steve Clamage) writes:

|> In article f...@gabi-soft.fr, ka...@gabi-soft.fr (J. Kanze) writes:
|> >Stephen.Clamage@Eng (Steve Clamage) writes:
|> >
|> >|> >(This leads to a further question: is a static_cast FROM void* legal?
|> >|> >Should it be?)
|> >|>
|> >|> Yes, since a static cast can reverse most implicit conversions. (I think
|> >|> the only exception is reversing a conversion to a virtual base class.)
|> >|> In particular, you can static_cast any data pointer to void* and back to
|> >|> the original type and get a pointer that compares equal to the original.
|> >

|> >This answers the first question. But should it be. Practically, it
|> >means that static_cast can be used to cast between arbitrary pointer
|> >types, since there is an implicit cast from any pointer type to void*,

|> >which will allow the compiler to invoke the static cast. I don't think
|> >that this is what the committee wanted, and IMHO, it breaks one of the
|> >largest single reasons for the new cast syntax: a "sideways" cast within
|> >a class hierarchy is no longer an error.
|>
|> I'm not sure what you are trying to say.

That I'm confused:-). Jason Merrill has since posted, stating that no
implicit conversions may occur on the argument of a static cast; I've
been unable to locate text to this effect in the draft. This would
solve the problem.

|> You can't use static_cast
|> to cast directly sideways in a hierarchy. That is, given
|> class VB { ... };
|> class A : public virtual VB { ... };
|> class B : public virtual VB { ... };
|> class C : public A, public B { ... };
|> A* pa = new C;
|> B* pb = static_cast<B*>(pa); // ERROR

I know that this is the intent; it is one of the major motivations for
the new style casts. My impression (to be confirmed) is that in fact,
since a static_cast<B*>( void* ) is legal, and the compiler can
implicitly convert pa to a void*, this line becomes illegal.

If someone can show me the place in the standard which confirms Jason's
assertion, I'll be happy. If not, it is an adequate solution (I think),
and perhaps should be adopted.

|> An old-style cast here would not result in an error message (it is
|> treated as a reinterpet_cast under the new rules, and is quietly accepted
|> under the old rules), but would not give a useful answer. The static_cast
|> must yield an error. Isn't that what we want?
|>
|> You can write the "double cast"
|> B* pb = static_cast<B*>(static_cast<void*>(pa));
|> which is perhaps your complaint.

My complaint (or rather my worry -- let's not call it a complaint until
we're sure that I'm right) is that I think that the compiler will do the
inner static cast implicitly, without my writing it.

|> But programmers must be educated
|> never to write code like this, just as they must be educated to
|> avoid old-style casts: you can easily wind up with syntactically
|> valid code with unknown or even undefined semantics.
|>
|> Safe casts in C++ may all be implicit. If you write an explicit cast,
|> it should always be considered a flag that something unportable might be
|> occuring. Many of the new-style casts are in fact safe if they compile.
|> A "double cast", it seems to me, must always signal something dangerous
|> and definitely unportable, even moreso than a reinterpret_cast.

Agreed. The next step would be to make the opposite true: implicit
casts in C++ are always safe:-). Of course, this would break some
common (and safe) C idioms: assigning an int to a char is definitly not
safe in the general case, but if the int is a return value from getc,
and I've already tested for EOF, it is safe, and is standard practice.

(This actually leads to a second question: is it guaranteed that I can
assign a non-EOF return value of getc, or istream::get, to a char, then
convert the char to unsigned char, and get the same results? I don't
think so: the conversion to char can still be "lossy". This is
definitly the case in C, in any case.)

Anyway, that's another question. While I think that there are
weaknesses here, they're inherited from C, we're not going to fix them
in this version of the standard, and market presure will ensure that in
fact, my programs will work on all commercial compilers, even if the
standard doesn't guarantee it.

GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France


-- Conseils en informatique industrielle --

Jason Merrill

unread,
Feb 28, 1997, 3:00:00 AM2/28/97
to

>>>>> John E Potter <jpo...@falcon.lhup.edu> writes:

> Jason Merrill (ja...@cygnus.com) wrote:

> : Except that implicit conversions are not performed on the operand of
> : static_cast in order to find a type that can be cast from; the new-style
> : casts only perform a single conversion.

> I like that. But does CD2 say that?

On the contrary, it does not say that implicit conversions are performed,
so they are not.

> I find in 4/5 that in some cases, standard conversions are suppressed
> in some contexts and that exceptions are given in the descriptions.

That paragraph is non-normative.

> In 5.2.9/4 exceptions are given for static_cast<void>. It seems that
> these exceptions would not be required if all standard conversions
> were suppressed.

Those exceptions are to 5/8.

I will readily agree that the WP is unclear on this point. However, the
various operators that do perform implicit conversions say the operands
"shall" be of some type. I suggest that this implies the implicit
conversions, and no such language appears in the description of
static_cast.

Jason
---

John E. Potter

unread,
Mar 1, 1997, 3:00:00 AM3/1/97
to

Jason Merrill (ja...@cygnus.com) wrote:

: >>>>> John E Potter <jpo...@falcon.lhup.edu> writes:

: > Jason Merrill (ja...@cygnus.com) wrote:

: > : Except that implicit conversions are not performed on the operand
: > : of static_cast in order to find a type that can be cast from; the
: > : new-style casts only perform a single conversion.

: > I like that. But does CD2 say that?

: On the contrary, it does not say that implicit conversions are performed,
: so they are not.

: > I find in 4/5 that in some cases, standard conversions are suppressed
: > in some contexts and that exceptions are given in the descriptions.

: That paragraph is non-normative.

Yep. No ;-[ on paragraph 5 and I missed the ;-] at the end which matched
the ;-[ at the beginning of paragraph 4. And the example of unary & does
not in fact list any exceptions. Since it expects an lvalue, it would be
redundant to exclude lvalue to rvalue conversions.

: > In 5.2.9/4 exceptions are given for static_cast<void>. It seems that


: > these exceptions would not be required if all standard conversions
: > were suppressed.

: Those exceptions are to 5/8.

Yep. Didn't read the line above correctly the first time; however, I
found 5/8 myself. Now I'm off to find another counter example...

: I will readily agree that the WP is unclear on this point. However,


: the various operators that do perform implicit conversions say the
: operands "shall" be of some type. I suggest that this implies the
: implicit conversions, and no such language appears in the description
: of static_cast.

... Many hours of enjoyable reading later.

Sold! Thanks for the education. Since you educated an educator, it
may have a ripple effect. Reading the CD2 is now like reading a
mystery novel for the second time. All (well most) of the clues are
obvious. Still not much of a plot though.

One part of this thread has been the unsafety of static_cast.
Paragraphs 5-9 (6 void* ==> anything*) are all things which if
used properly are quite safe and reasonable. Like anything in the
language, they can be misused. With this issue cleared up, I am
content with the relative safety of static_cast. I will pick up the
other part, which involves safe things which can not be done, elsewhere.

John

0 new messages