tags:

views:

85

answers:

3

Hello. Could anyone please explain the following line of code, found on http://docs.openttd.org/ai__cargo_8cpp_source.html

return (AICargo::TownEffect)::CargoSpec::Get(cargo_type)->town_effect;

If this line was:

return (AICargo::TownEffect) ::CargoSpec::Get(cargo_type)->town_effect;

(note the space between TownEffect) and the ::) then I would understand it fine. However there is no whitespace in that document*, which would mean (AICargo::TownEffect) is the left operand of the :: operator.

How does this code work/compile? Or are the two things equivilent due to some obscure C++ rule?

*It's the same in the cpp file as well.

+7  A: 

Other than separating tokens, white space is generally not significant in C++ grammar.

Parentheses are significant, and they can't appear in a qualified-id so there is no equivalence between:

(AICargo::TownEffect)::CargoSpec::Get

and

AICargo::TownEffect::CargoSpec::Get

In the first there are two qualified-ids, one in parentheses naming a type and the other naming a function. The only valid interpretation of a parenthesized type in this context is as a cast-expression. Whether there is a space after the closing parenthesis makes no difference.

Charles Bailey
Technically, the `AICargo::TownEffect` is a _type-id_ and not a _qualified-id_ because it names a type and not an object or function.
Charles Bailey
+1  A: 

It's a simple problem of parsing: the whitespace is not necessary here because we know that the C-style cast ends with the parenthesis.

It's no more unreadable that:

if(cargo_type){return cargo_type->town_effect;}

It probably stems from the fact that ( and ) cannot be part of an identifier.

Matthieu M.
+1  A: 

Charles is right above when he says that there can be no parentheses in a qualified-id.

I want to add that in C++ you shouldn't be using the old C-style casts as a matter of style. They're typically stronger than you want and cast away constness, which is very often not what you want. Additionally, they're effectively impossible to search your codebase for, making it hard to review expressions that typically are much more likely to cause bugs.

Instead, in this case, if you actually need the full power of a C-style cast (disregarding constness at the moment), you should use reinterpret_cast<AICargo::TownEffect>. Without looking at the code, though, it would not surprise me if a static_cast were sufficient.

jemfinch
Though it should be noted that `static_cast` does not increase safety by much, perhaps that a `dynamic_cast` should be substituted in debug mode.
Matthieu M.
On the contrary: to quote Stroustrup, "The *static_cast* operator converts between related types such as one pointer type to another in the same class hierarchy, an enumeration to an integer type, or a floating-point type to an integral type. The *reinterpret_cast* handles conversions between unrelated types such as an integer to a pointer or a pointer to an unrelated pointer type. This distinction allows the compiler apply some minimal type checking for *static_cast* and makes it easier for programmers to find the more dangerous conversions represented as *reinterpret_cast*s."
jemfinch
@Matthieu M.: `dynamic_cast` and `static_cast` do completely different things. Using them interchangeably (e.g. in different build configurations) is asking for trouble. If the release build correctly uses `static_cast`, then so should the debug build. The only time you might want a `dynamic_cast` in a debug build but not in a release build would be inside an assert.
Charles Bailey