Topic: Casts: What are the differences?


Author: swf@elsegundoca.ncr.com (Stan Friesen)
Date: 1997/02/28
Raw View
In article <5evq3p$c6m@news.csus.edu>, dick@silicon.csci.csusb.edu (Dr.
Richard Botting) writes:
|> I could do with a simple briefing describing the differences between
|>  a cast

Any cast: any specific instance of a regular cast is equivalent to
one of the following types of cast except a dynamic_cast, possibly
followed by a const_cast.

|>  a constant cast

A cast that adds or removes the specifier 'const' from a type, but makes
no other changes.

|>  a static cast

A "safe" cast.  Performs casts that would be legal as initializations
of variables, along with the inverses of any standard (implicit)
conversion.  Uses only compile-time information to do the conversion.

[This means that static_cast<Derived *>(base_ptr) is legal, but dereferencing
it may cause undefined behavior, if the real dynamic type of the object
pointed to is not 'Derived'.]

|>  a dynamic cast

Uses run time type information to perform a cast of pointers or references
to class type.
In particular, casts to derived classes, or casts sideways in a class lattice
are allowed, but they only succceed if the real type of the object pointed or
referred to actually *is* of the target type.

A dynamic cast of a pointer type returns NULL if it fails.

A dynamic cast of a reference type throws bad_cast if it fails.

The result is that ypu can safely actually *use* the results of a successful
dynamic cast.

|>  a reinterpret cast

Any other legal cast.
The results are, except for casts to void*, implementation defined,
and are rarely portable or even safe.

--
swf@elsegundoca.ncr.com  sarima@ix.netcom.com

The peace of God be with you.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/28
Raw View
Dr. Richard Botting wrote:
>
> I'm feeling like I'm lost in twisty maze of little passages
> that all look alike....  Or was it a maze of little twisty passages
> that all look alike?
>
> In the December Draft/HTML Version I find:
>
>   5.2.7  Dynamic cast                                [expr.dynamic.cast]
> 1 The  result of the expression dynamic_cast<T>(v) is the result of con-
>   verting the expression v to type T.  T shall be a pointer or reference
>   to a complete class type, or "pointer to cv void".  Types shall not be
>   defined in a dynamic_cast.  The dynamic_cast operator shall  not  cast
>   away constness (_expr.const.cast_).
>
>   5.2.9  Static cast                                  [expr.static.cast]

[The same as dynamic-cast; skipped]

>
>   5.4  Explicit type conversion (cast notation)              [expr.cast]

[Nearly the same; skipped]

>
> I looked in my C++.FAQ but they didn't help too much.
>
> I could do with a simple briefing describing the differences between
>         a cast
>         a constant cast
>         a static cast
>         a dynamic cast
>         a reinterpret cast

You should have read the others paragraphs of the specific
clauses ! The fisrt one is pretty boring and doesn't tell
much.

Summary of new-style casts
--------------------------

- static_cast: tell the compiler that you lost static type
  information and want to recover it; the compiler should
  trust you, but (as usual in C++), if you lie, you get
  undefined behaviour; can only be used to recover info
  on the un-qualified type, not to recover cv-qualifier
  info.
  -> See const-cast for that
  -> See dynamic-cast when you don't want to tell the
     compiler to trust you

  static_cast is also appropriate for conversion betwen
  numeric types

Example:

Base  b;
Der   d;
Base *b1 = &d, // you loose type info in the standard conversion
     *b2 = &b;
Der*  dp = static_cast<Der*> (b1); // OK
dp = static_cast<Der*> (b2); // Please don't lie like that;
// undefined behaviour

The standard: clear, except about void* -> T* conversions which
have IMO been forgoten.

- const_cast: nearly the same with cv-qualification, with
  a difference: you can lie if you want

int        i;
const int  ci = foo;
const int *pic1 = &i, // you loose type info in the standard conversion
          *pic2 = &ci;
int*       pi = const_cast<int*> (pic1); // OK
pi = const_cast<int*> (pic2); // not true, but OK
*pi = 2; // don't write on a const ! undefined behaviour

IMO this is needed to use a const-unsafe library.

In these two cases you must ensure by other means that
the dynamic type (resp. dynamic constness) is what you
say it is (if you want to write to the variable in the
case of the const_cast).

This is the same as an union with a discriminant field:

struct I_know_what_I_contain {
    enum { nothing, an_int, a_float } what;
    union {
        float f;
        int i;
    };
};

It's up to you to access the right field using 'what'.

- dynamic_cast<T*>: you ask if the object has a dynamic
  type convertible to T; the compiler do the conversion if
  so or return 0. Abuses of dynamic_cast lead to very bad
  style and inneficient programs.

- reinterpret_cast: used to convert from unrelated pointer
  types and to put a pointer in an integer and back;
  implemantation defined execpt that reinterpret-casts
  are value preserving when you go back to the original
  type (if the intermediate type is large enough).

The standard: IMO reinterpret_casts is 10% handled by
the DWP, and by 90% by quality of implementation; while
it is clear that reinterpret_cast is intended to be used
for undefined/unspecified tricks, it should IMO be
converded better by the DWP.

In particular reinterpret_cast<[const] char*> should have
some meaning (other than implementation defined).

Also all the 'layout-compatible' stuff is pointless
if you can't portably cast betwen incompatibles types.

struct {
   int i; // first member
} s;
*(reinterpret_cast<int*> (&s)) = 0;

struct {
   float f;
   int i; // any member
} s;
*(reinterpret_cast<int*> (&s)) = 0;

I'm not sure if the above stuff was guarantied to work
(with old style cast, ARM or ANSI C), but if it was,
it's no more guarantied since reinterpret_cast return
an unspecified value.

Old style casts
---------------

Old style casts are:
- if they convert betwen numeric types -> static_cast
- if they navigate a class hierarchy (to Base or to Der)
  -> static_cast
- if they convert betwen unrelated types
  -> reinterpret_cast

Also if cv-qualification is removed, a const_cast is added.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu                             ]





Author: dick@silicon.csci.csusb.edu (Dr. Richard Botting)
Date: 1997/02/26
Raw View
I'm feeling like I'm lost in twisty maze of little passages
that all look alike....  Or was it a maze of little twisty passages
that all look alike?

In the December Draft/HTML Version I find:

  5.2.7  Dynamic cast                                [expr.dynamic.cast]
1 The  result of the expression dynamic_cast<T>(v) is the result of con-
  verting the expression v to type T.  T shall be a pointer or reference
  to a complete class type, or "pointer to cv void".  Types shall not be
  defined in a dynamic_cast.  The dynamic_cast operator shall  not  cast
  away constness (_expr.const.cast_).

  5.2.9  Static cast                                  [expr.static.cast]
1 The result of the expression static_cast<T>(v) is the result  of  con-
  verting  the  expression  v  to type T.  If T is a reference type, the
  result is an lvalue; otherwise, the result is an rvalue.  Types  shall
  not  be  defined in a static_cast.  The static_cast operator shall not
  cast away constness (_expr.const.cast_).

  5.4  Explicit type conversion (cast notation)              [expr.cast]
1 The result of the expression (T) cast-expression is of  type  T.   The
  result  is an lvalue if T is a reference type, otherwise the result is
  an rvalue.

I looked in my C++.FAQ but they didn't help too much.

I could do with a simple briefing describing the differences between
 a cast
 a constant cast
 a static cast
 a dynamic cast
 a reinterpret cast

--
dick botting     http://www.csci.csusb.edu/dick/signature.html
Disclaimer:      CSUSB may or may not agree with this message.
Copyright(1997): Copy freely but say where it came from.
 I have nothing to sell, and I'm giving it away.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@Eng.Sun.COM (Steve Clamage)
Date: 1997/02/26
Raw View
In article c6m@news.csus.edu, dick@silicon.csci.csusb.edu (Dr. Richard Botting) writes:
>I'm feeling like I'm lost in twisty maze of little passages
>that all look alike....  Or was it a maze of little twisty passages
>that all look alike?
>
>In the December Draft/HTML Version I find: ...
>
>I could do with a simple briefing describing the differences between
> a cast
> a constant cast
> a static cast
> a dynamic cast
> a reinterpret cast

I deleted the quotes from the draft, which were the introductory
paragraph describing each kind of cast. The introductory paragraphs
are (intentionally) nearly identical. The remainder of each section
describes its cast in detail.

The draft standard uses 7 pages to describe all the casts. I don't
think a complete explanation could be much smaller, so I'll use
the pedagogical device of oversimplification to keep things down
to the size of a netnews article. First I'll provide a brief
rationale for introducing the four new casts.

The old-style cast could be used for several different purposes. In
particular, it was commonly used for
 - changing constness: (char*)ptr_to_const_char
 - converting a value to a value of a different type: (int)floating_value
 - navigating a hierarchy: (derived*)ptr_to_base
 - type "punning": (int*)ptr_to_void
(Type punning generally means treating an object, particularly a
pointer, as an object of a different type.)

When the notations (T)e and T(e) are both allowed, they are exactly
equivalent. Sometimes only one of the notations is allowed.

When you see a cast expression like "(T)(expr)" it is often hard to
tell why the cast is there. Also, it isz often hard to tell whether
the cast is likely to be dangerous or nonportable. Finally, there is
no way to scan a source file and reliably find every cast, unless you
have a "smart editor" that can fully parse C++ source code.

C++ introduced four new cast operators to address those problems: Each
cast is intended for a particular purpose (although there is some
overlap), and each cast uses a new keyword that can be found easily
with any text editor.

Here is an oversimplified description of each cast. In particular,
I'm ignoring references and discussions of lvalue vs rvalue.
(Generally speaking, what is true for pointers is true for references.
Generally speaking, casting yields an rvalue, except that casting
a reference generally yields an lvalue.)

static_cast: primarily for value conversion
- can perform any conversion which is allowed implicitly
- can reverse any implicit conversion except cannot downcast from a
   virtual base class
- cannot remove constness

dynamic_cast: primarily for hierarchy navigation
- can upcast a ptr_to_derived to any of its unambiguous base classes
- can downcast a ptr_to_base (including virtual base classes) to any
   unambiguous derived class if the class is polymorphic. Indicates
   failure if the object isn't really the right type.
- can cast "sideways" in a hierarchy if the class is polymorphic
- cannot remove constness

reinterpret_cast: primarily for type punning
- can convert pointers to integer types and vice versa
- can convert a pointer to an unrelated pointer type
- results are implementation-defined and might not work
- cannot remove constness

const_cast: primarily to remove constness
- can change the const and volatile specifiers of a type
- cannot otherwise change the type

The old-style cast is now defined as a static_cast or reinterpret_cast
possibly followed by a const_cast. (The only dynamic_casts that an
old-style cast can perform can also be performed by a static_cast.
If an old-style cast could be performed by both a static_cast and a
reinterpret_cast, the static_cast interpretation is chosen.)

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Michael Hudson <sorry.no.email@nowhere.com>
Date: 1997/02/27
Raw View
As you can't understand much of what goes in comp.std.c++ without
understanding this, here goes:

Dr. Richard Botting wrote:
>
> I could do with a simple briefing describing the differences between
>         a cast

A (C-style) cast maps to one of the new-style casts. Which one (or more
than one), however, depends.

I think it tries to become a static_cast, and if that fails it becomes a
reinterpret_cast, either of which may be combined with a const_cast.

>         a constant cast

A const_cast changes the const/volatile qualification of its parameter,
and nothing else.

>         a static cast

A static_cast basically performs any type conversions that would be
possible any way.
This is handy because
a) sometimes you want to disambiguate a function call
and
b) it can perform them backwards.

>         a dynamic cast

A dynamic_cast casts around a class heirachy safely.

>         a reinterpret cast

A reinterpret_cast is used to perform anything that can't be done any
other way. Use of reinterpret_cast's requires watching. It has been said
that any use of reinterpret_cast in a program is a sign of fundamental
flaws in the program, but I think this is a little strong.

>
> --
> dick botting

--
Regards,
    Michael Hudson

Please don't email this address - it's not mine.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]