2

I just came across something I don't quite understand. I thought that static_cast<TYPE>(variable) was equivalent (or better/safer) than TYPE(variable). However, the following code DOES not work

HMENU hMenu = CreateMenu();
HMENU hSubMenu = CreatePopupMenu();

// File
AppendMenu(hSubMenu, MF_STRING, WndClass_main::ID_FILE_EXIT, "&Quit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, static_cast<intptr_t>(hSubMenu), "&File");

My compiler says that it can't convert from HMENU to intptr_t. I have a 64-bit system btw, which interfers with the casting between void* and int? However, from what I understand the type intptr_t (defined in cstdint) is guaranteed to be big enough for a void*.

The interesting part is that the following (note the different cast) works:

HMENU hMenu = CreateMenu();
HMENU hSubMenu = CreatePopupMenu();

// File
AppendMenu(hSubMenu, MF_STRING, WndClass_main::ID_FILE_EXIT, "&Quit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (intptr_t)(hSubMenu), "&File");

What am I missing?

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
jensa
  • 2,792
  • 2
  • 21
  • 36
  • 6
    You are missing the different between a `static_cast` and a `reinterpret_cast` – quantdev Nov 25 '14 at 20:06
  • C's cast syntax is `(TYPE)variable` -- or, more precisely, `(` *`type-name`* `)` *`cast-expression`*. The `TYPE(variable)` syntax is specific to C++. – Keith Thompson Nov 25 '14 at 20:14
  • It is indeed better/safer than function-style or C-style casts, in that it doesn't allow dodgy conversions like this one. – Mike Seymour Nov 25 '14 at 20:20

2 Answers2

5

I thought that static_cast<TYPE>(variable) was equivalent (or better/safer) than TYPE(variable).

It's just a subset. Some conversions can be performed by both, but some can only be performed by the latter. For function-style casts the standard specifies in [expr.type.conv]:

If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).

That is, TYPE(variable) is equivalent to (TYPE)variable. Now for the latter the standard specifies

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.

(Note that there is an additional text explaining some differences concerning class hierarchies, which is not quite relevant here.)
In your case, static_cast does not suffice. reinterpret_cast would, since it can convert integers to pointers and vice versa. One of the reasons why reinterpret_cast should then be preferred is e.g. the ability of being found by a search for potentially dangerous casts. For further queries on when to use both, see this question.

Community
  • 1
  • 1
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • And that is, of course, assuming that `TYPE` in `TYPE(variable)` is a primitive type and not a class with a constructor. – Mike DeSimone Nov 25 '14 at 20:18
  • @MikeDeSimone What is "that"? If you mean the equivalency of the casts: Not really. Why would we have to make that assumption? – Columbo Nov 25 '14 at 20:23
  • 1
    @MarkRansom What can I say? Everything tells me you're wrong, including [**this code snippet**](http://coliru.stacked-crooked.com/a/ac0cdb1ca5ba0468). – Columbo Nov 25 '14 at 20:47
  • Interesting. I found [this question](http://stackoverflow.com/questions/1384007/conversion-constructor-vs-conversion-operator-precedence) which asks which will be called, but when I edit your test snippet to make the type conversion operator `const` it doesn't change the result as the answer suggests it should. Either way I'm deleting my earlier comment because it's clearly wrong. – Mark Ransom Nov 25 '14 at 23:26
2

It is safer. It is also weaker. It is safer, because it is weaker.

A static_cast will do implicit casting, plus it will cast a pointer down a class hierarchy.

It will also cast a pointer to/from void* with the same const volatile qualifications. (from I believe is implicit in C -- it is not in C++).

These are the "safer" set of casts in C/C++.

const_cast and reinterpret_cast gets the more dangerous ones. These include converting a pointer value to an integer, even if the integer is big enough.

My advice is to write this function:

intptr_t ptr_to_intptr( void* p ) { return reinterpret_cast<intptr_t>(p); }

which makes the operation clear, then use it.

Unlike (intptr_t) it will only work on pointers -- so if you accidentally fed it something that isn't a pointer. Or a pointer you cannot store in an intptr_t like a pointer-to-member.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524