Which is the idiomatic way to document a static_cast?

367 views Asked by At

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.

3

There are 3 answers

0
Steve Jessop On BEST ANSWER

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.

5
John Dibling On

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.

3
Yakk - Adam Nevraumont On

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.