Is the *only* purpose of a *function signature* (a

2019-02-22 06:00发布

问题:

Related to Why does casting a function to a function type that is identical except for return type fail?, I would like to understand, in a fuller way, the distinction between a function's type and a function's signature.

For example, the type of a function must typically be considered when dealing with function pointers, and the type of the function includes the return type of that function.

However, as noted in Mike Seymour's answer to the above-linked question, the signature of a function is different from the type of a function. The signature is certainly used to disambiguate from among potential overloaded functions (noting that the return type of functions does not play a role in identifying unique functions). But, I would now like to understand the relevance and importance of function signatures vs. function types. It occurs to me that the only purpose of function signatures in C++ is to identify overload candidates and/or unique functions in an overload set, during overload resolution.

Am I correct? Is overload resolution the only purpose of function signatures in C++? Or are there any other uses/applications of function signatures, besides (or only indirectly related to) overload resolution?

ADDENDUM For clarity, please note that I am specifically seeking to understand the distinction between the purpose of a function signature and a function type. I.e., I know that a function type is required both for the use of function pointers, and for a compiler/linker's implementation of a calling convention. However, the calling convention is relevant only after overload resolution is complete. I am here asking, specifically, if the only purpose of the function signature (as opposed to type) is for overload resolution.

回答1:

Am I correct?

As far as I'm concerned, there are other purposes too. Consider that C also has function signatures but doesn't have overloading.

Apart from overloading, the fundamental purpose of function signatures is conforming to the calling convention of a particular platform.

When a function accepts arguments and returns values, the compiler needs to know the type and the size of the arguments in order to pass them correctly to a function. In general, function arguments are pushed onto the stack (this is not a universal rule though, especially on 64-bit architecture systems). Consider the following situation. If you call a function like

foo(42);

how does the compiler know what is the size of the integer value it has to pass to the function? The number 42 can be represented using various bit width, for example as a 1, 2, 4 (or even 8)-byte integer:

00101010
0000000000101010
00000000000000000000000000101010

Now if the function doesn't have a signature which tells that, for instance, the argument is a char (which is 1 byte), or a short (which may be 2 bytes) or an int, which may be 4 bytes, then the compiler has no way of determining the correct size. It means that if it pushes an arbitrary number of bytes to the stack, but the function expects another size, then stack corruption occurs.

Another good example is returning structures (struct). Usually, primitive return values (such as integers and floating-point numbers) are returned in a register; this is generally the EAX register on x86. But what if one wants to write a function returning a struct? if the overall size of the struct is so large that it doesn't fit into a register, the compiler must generate code that pushes the return value onto the stack as opposed to assigning it to a register. So if a function is defined as

int foo()
{
    return 1337;
}

or as

struct bar {
    int a;
    char b[16];
    float x;
};

struct bar foo()
{
    struct bar ret;
    ret.a = 0;
    memcpy(&ret.b, "abcdefghijklmno", sizeof(ret.b));
    ret.x = 3.1415927;
    return ret;
}

different assembly (and machine code) will be generated - the first function that returns an integer will use the EAX register for storing the return value, but the second call will have to use the stack.



回答2:

The standard mentions that signatures are used for name mangling and linking.

That being said, name mangling is not standarized. The return type is redundant in a function symbol (since there is only one possible return type for a function with a given name and arguments in a valid program, it is not required to differentiate two different symbols), but even then some ABIs do include the return type of a function in the mangled name, probably as a way of double checking that there is no violation of the rule above.