-1

I understand that (MyType)foo, MyType(foo), and static_cast<MyType>(foo) are somewhat similar in that the first two become the last (EDIT: I have been corrected. The previous sentence is untrue.) My question is which is the idiomatic version to use? Is context a consideration?

Since I know that C style casts are not preferred in C++, it is down to functional casts and static_cast.

griotspeak
  • 13,022
  • 13
  • 43
  • 54

3 Answers3

3

Using an explicit cast is idiomatic in my book.

static_cast<MyType>(foo) is better than (MyType)foo because it is more expressive and explicit. This is especially useful in the many cases where the (MyType)foo doesn't mean what you expect it to mean. For example, in your examples these cases might not reduce to static_cast at all, but reinterpret_cast.

In a code review, I would reject every and all C-style casts on non-arithmetic types.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 1
    I'd allow them for arithmetic types. I don't think that `std::tolower(static_cast(c));` is any better than `std::tolower((unsigned char)c);`. The destination type is enough to ensure it can't remove const or anything else scary. But if you need a very simple rule, then it has to be to forbid them. IIRC there's also some stupid thing that can only be done with a C-style cast but that in almost all circumstances you have no business doing anyway. Casting a pointer to a pointer-to-private-base or something. – Steve Jessop Dec 10 '13 at 16:40
  • @SteveJessop: I wasn't really thinking of arithmetic types. I suppose I more or less agree with you. Less in the sense that the type of `i` might be changed in the future, but that seems a little fantastical. – John Dibling Dec 10 '13 at 16:42
  • @SteveJessop: Right. – John Dibling Dec 10 '13 at 16:47
  • sorry, deleted the comment from under you. I had a sudden panic what would happen if it changed to a pointer type (you'd want the code to fail to compile since even though the `tolower` call is "safe" it's meaningless), but I don't have time to check right now :-) – Steve Jessop Dec 10 '13 at 16:50
  • @SteveJessop: Right again, but like I said earlier, it seems a bit fantastical. FWIW, I do use explicit casts for arithmetic types, but I don't reject C-style casts on them in a code review. I may start doing the latter. Better always safe than very rarely sorry, I suppose. – John Dibling Dec 10 '13 at 17:00
2

First, while new to C++, functional style casts are C-style casts. They do the same thing.

C-style casts first try to static_cast, and if that doesn't work, does a reinterpret_cast1. This is bad, because operations that are intended to be a simple change can become very strange.

As an example, suppose you have a Foo* f and a completely unrelated class Bar. You can (Bar*)f and it will silently reinterpret the *f to be a Bar, likely leading to undefined behavior.

A static_cast would only work in this situation if Foo and Bar were related types.

Now, even static_cast can be too strong, so I often write up an implicit_cast or non_cast template that converts-without-casting. Similarly, I write as_const that adds const rather than requiring a const_cast (which can both add and remove const, and removing const is far more dangerous than adding it).

It is somewhat annoying that there is no explicit safe cast in C++, and I do not know how to write one (one that will execute explicit constructors, but will not cast "down" a type hierarchy like static_cast will).


1 this is an over-simplification, but close enough to true.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • "there is no explicit safe cast in C++" -- hmm. A template function that does a `static_cast` and is disabled where both template parameters are pointer types and (after `remove_pointer`) the source is a base of the destination? – Steve Jessop Dec 10 '13 at 16:37
  • @SteveJessop "upward" pointer casts are also safe, so ya. And SFINAE will probably work. Does that catch every case? – Yakk - Adam Nevraumont Dec 10 '13 at 17:20
  • Btw, a web search for “C++ explicit_cast” led me there: http://stackoverflow.com/questions/5693432/making-auto-cast-safe (in short, C++11 uniform initialization and `std::is_constructible` were evoked) – gx_ Dec 10 '13 at 17:29
2

The first two, (MyType)foo and MyType(foo), have the same meaning. What the meaning is depends a lot on MyType and the type of foo. So for example:

((int)1.0);   // a static_cast
int i = 0;
((char*)&i);  // a reinterpret_cast
const int j = 0;
((int*)&j);   // a const_cast
((char*)&j);  // a combination of a const_cast and a reinterpret_cast

typedef char *CharPtr;
CharPtr(&j);  // a C-style cast, same as ((CharPtr)&j) or ((char*)&j)

So the reason C-style casts are not preferred is that C-style casts do a lot of different things. C++ provides means to distinguish between them. In a lot of cases that makes it easier to understand the code, especially once you're writing generic code in C++ and the types are less obvious than they typically are in C.

So that's why static_cast etc are preferred in C++ -- they lay out explicitly for the reader something that might otherwise be difficult for them to work out for themselves. static_cast also does a few different things, by the way, so in principle you might want even more different casts. But static_cast at least does less than a C-style cast.

Functional-style casts with one argument are C-style casts. They are explicitly defined in the standard to mean exactly the same thing, in all cases.

Now, there are some cases where you might accept a C-style cast when written in the functional syntax but not when written with the C cast syntax. This is generally when the destination type is a class and it's obvious which constructor will be called. In a case like that, std::vector<int>(0) is shorter than static_cast<std::vector<int>>(0), and generally clearer since it can be read as a constructor call. But it still has the same "dangers" as writing (std::vector<int>)0. If you were to write every conversion that way then eventually you'd accidentally remove const from a pointer type.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699