In addition to the other already-good answers, I want to add the following answer, which I believe is even closer to the "real" answer Linus had in mind.
Of course you can not look at the function and immediately know all consequences. The real point is that there is a procedure you can follow which will let you determine the answer.
1. Locate the "write" function. There will only be one, because C has no namespaces.
2. Read the "write" function.
3. Repeat recursively as needed (including for macros).
For C++, the equivalent would be something like fd->write(buf), and the procedure is:
1. Determine the type of 'fd'.
2. Determine what subtypes you could have there and which you might actually have in hand. Or is the class not virtually inherited in which case it doesn't matter?
3. Determine what the write method does. In order to do so, have intimate knowledge of all operator overloading any value used in the write method may have.
4. Figure out what "buf" is and whether it magically overloads other operators.
write(fd, buf, size) resolves to one function with three arguments that themselves can't be that magical, and usually one basic approach to the question of memory management. fd->write(buf) involves classes, inheritance, potentially overloads, interfaces that 'buf' may correspond to and the potential need to follow a chain of some number of functions just to see whether that was constructed automatically into another type, endless permutations of how memory may be handled, and so on and so forth. The assembler that the C code will generate will basically push three arguments and call a function; the assembler the C++ generates is effectively unbounded in complexity. This assembler complexity directly corresponds to complexity that must be understood in order to understand the line of code.
In general I'd prefer the C++, but when writing a kernel where every twitchy detail counts for everything and the slightest bit of "wrong" could be a rootable security bug, I see the counterarguments.