Basically speaking, the rule simply doesn't work, or else it
works by redefining what is meant by spiral (in which case,
there's no point in it. Consider, for example:
int* a[10][15];
The spiral rule would give a is an array[10] of pointer to
array[15] of int, which is wrong. It the case you cite, it
doesn't work either; in fact, in the case of signal
, it's not
even clear where you should start the spiral.
In general, it's easier to find examples of where the rule fails
than examples where it works.
I'm often tempted to say that parsing a C++ declaration is
simple, but nobody who has tried with complicated declarations
would believe me. On the other hand, it's not as hard as it is
sometimes made out to be. The secret is to think of the
declaration exactly as you would an expression, but with a lot
less operators, and a very simple precedence rule: all operators
to the right have precedence over all operators to the left. In
the absence of parentheses, this means process everything to the
right first, then everything to the left, and process
parentheses exactly as you would in any other expression. The
actual difficulty is not the syntax per se, but that it
results is some very complex and counterintuitive declarations,
in particular where function return values and pointers to
functions are involved: the first right, then left rule means
that operators at a particular level are often widely separated,
e.g.:
int (*f( /* lots of parameters */ ))[10];
The final term in the expansion here is int[10]
, but putting
the [10]
after the complete function specification is (at
least to me) very unnatural, and I have to stop and work it out
each time. (It's probably this tendency for logically adjacent
parts to spread out that lead to the spiral rule. The problem
is, of course, that in the absence of parentheses, they don't
always spread out—anytime you see [i][j]
, the rule is go
right, then go right again, rather than spiral.)
And since we're now thinking of declarations in terms of
expressions: what do you do when an expression becomes too
complicated to read? You introduce intermediate variables in order
to make it easier to read. In the case of declarations, the
"intermediate variables" are typedef
. In particular, I would
argue that any time part of the return type ends up after the
function arguments (and a lot of other times as well), you
should use a typedef
to make the declaration simpler. (This
is a "do as I say, not as I do" rule, however. I'm afraid that
I'll occasionally use some very complex declarations.)