what is the difference between function declaration and signature? - c++

In C or C++ what is the difference between function declaration and function signature?
I know something of function declaration but function signature is totally new to me. What is the point of having the concept of function signature? What are the two concepts used for actually?
Thanks!

A function declaration is the prototype for a function (or it can come from the function definition if no prototype has been seen by the compiler at that point) - it includes the return type, the name of the function and the types of the parameters (optionally in C).
A function signature is the parts of the function declaration that the compiler uses to perform overload resolution. Since multiple functions might have the same name (ie., they're overloaded), the compiler needs a way to determine which of several possible functions with a particular name a function call should resolve to. The signature is what the compiler considers in that overload resolution. Specifically, the standard defines 'signature' as:
the information about a function that participates in overload resolution: the types of its parameters and, if the function is a class member, the cv-qualifiers (if any) on the function itself and the class in which the member function is declared.
Note that the return type is not part of the function signature. As the standard says in a footnote, "Function signatures do not include return type, because that does not participate in overload resolution".

The standard defines two terms: declaration and definition. A definition is a tentative declaration. However, the C99 and C++03 standards have slightly varying definitions.
From C++0x draft:
Appendix C
8.3.5 Change: In C++, a function declared
with an empty parameter list takes no
arguments. In C, an empty parameter
list means that the number and type of
the function arguments are unknown"
Definitions
1.3.11 signature
the name and the parameter-type-list
(8.3.5) of a function, as well as the
class, concept, concept map, or
namespace of which it is a member. If
a function or function template is a
class member its signature
additionally includes the
cv-qualifiers (if any) and the
ref-qualifier (if any) on the function
or function template itself. The
signature of a constrained member
(9.2) includes its template
requirements. The signature of a
function template additionally
includes its return type, its template
parameter list, and its template
requirements (if any). The signature
of a function template specialization
includes the signature of the template
of which it is a specialization and
its template arguments (whether
explicitly specified or deduced). [
Note: Signatures are used as a basis
for name mangling and linking.—end
note ]

The function signature doesn't include the return type or linkage type of the function.
OK, Wikipedia disagrees with me on the return type being included. However I know that the return type is not used by the compiler when deciding if a function call matches the signature. This previous StackOverflow question appears to agree: Is the return type part of the function signature?

Also please note that top-level const and volatile on argument are not part of the signature, according to the standard. But some compilers get this wrong.
e.g.
void f(const int, const char* const);
has the same signature as
void f(int, const char*);

A function declaration is a prototype. A function signature indicates what is the return type and the parameters used that makes up the signature. Consider this:
int foo(int, int); /* Function Declaration */
/* Implementation of foo
** Function signature
*/
int foo(int a, int b){
}
Now, consider this scenario: a programmer is asked what is the function signature for foo:
It returns a datatype of int
Two parameters are also of datatype of int, named a and b respectively
The function prototype on the other hand is to clue in the C/C++ compiler, on what to expect and if the signature does not match up with the prototype, the compiler will emit an error, along the context of 'function declaration error' or 'prototype mismatch'.

Related

Is the type of a function affected by the presence or absence of a function parameter pack in its parameter-declaration-clause?

Consider the following:
#include <type_traits>
void f(int);
void g(auto ...);
static_assert(std::is_same_v<decltype(f), decltype(g<int>)>); // succeeds in GCC 12.1, Clang 14.0.0, and MSVC 19.30
[dcl.fct]/12 specifies that the parameter-type-list of a function is part of the function type (obviously):
The return type, the parameter-type-list, the ref-qualifier, the cv-qualifier-seq, and the exception specification, but not the default arguments or the trailing requires-clause, are part of the function type.
The last sentence of [dcl.fct]/5 specifies that the "presence or absence of ... a function parameter pack" is part of the function's parameter-type-list:
The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list.
Since the non-template function f has no function parameter pack in its parameter list but variadic function template g does, would that not mean that their parameter-type-lists are different, and therefore their types are also?
Since the non-template function f has no function parameter pack in its parameter list but variadic function template g does, would that not mean that their parameter-type-lists are different, and therefore their types are also?
g is an Abbreviated Function Template. Being a template, it doesn't have a type of its own.
g<int>, on the other hand, is an instantiation of g. It produces a concrete function void g(int), which has the same type as f.

Overload function template by return type

I accidentally find the following two templates can be overloaded(don't incur a name redefined error), which I think is counter-intuitive.
template<typename T>
void func(T) {}
template<typename T>
int func(T) {return 0;}
From cppreference.com, there is a related paragraph:
When an expression that uses type or non-type template parameters
appears in the function parameter list or in the return type, that
expression remains a part of the function template signature for the
purpose of overloading:
But the return types of those two functions don't include T. Who can explain it for me?
Your paragraph quoted is irrelevant.
There is a special rule to prevent non-template functions that differ only in the return type from being overloaded (from the standard [over.load]/2.1):
Function declarations that differ only in the return type, the exception specification, or both cannot be overloaded.
So the program is ill-formed if such declarations exist (even if the program does not call them). However, this rule neither applies to function templates, nor to template specializations synthesized for the purpose of overload resolution according to [over.load]/1.
Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. A program is ill-formed if it contains two such non-overloadable declarations in the same scope. [ Note: This restriction applies to explicit declarations in a scope, and between such declarations and declarations made through a using-declaration. It does not apply to sets of functions fabricated as a result of name lookup (e.g., because of using-directives) or overload resolution (e.g., for operator functions). — end note ]
So these two templates can be well overloaded.
However, as Dean Seo said in his answer, if you try to call func, the program would be ill-formed due to the ambiguity of overload resolution.
the following two templates can be overloaded(don't incur a name redefined error), which I think is counter-intuitive.
Not really.
The two functions can't be overloaded, but the compiler just does not know their existence until the very moment of them being instantiated:
// Try invoking `func`
func(0xFF);
Now the compiler will throw an error message similar to:
error: call to 'func' is ambiguous

Function with default parameter as template type

I am trying to use a function with a default argument as a function pointer template parameter:
template <void (*F)()>
class A {};
void foo1(int a = 0) {}
void foo2() {}
int main()
{
//A<foo1> a1; <-- doesn't work
A<foo2> a2;
}
The compiler error is:
main.cpp:7:7: error: could not convert template argument ‘foo1’ to ‘void (*)()’
Is there specific syntax for this to work? Or a specific language limitation? Otherwise, the alternative is to have two separate functions instead of a default parameter:
void foo1(int a) {}
void foo1() { foo1(0); }
Update
I understand that the signatures are different, but I'm wondering if there is a way to make this work conveniently without needing to modify all the functions with default parameters?
The signature of foo1 is void(int), not void(). This is why it isn't convertible to void(*)().
You are confusing default arguments with overloading.
According to section 8.3.6 of the C++ standard,
If an expression is specified in a parameter declaration this expression is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
Since A<foo1> is not a call of the function, default arguments are ignored. In fact, they are ignored in all contexts except the calls of the function, for example
typedef void (*FFF)();
FFF x = foo1;
will not compile, and produce the same message that you get when trying to use foo1 as a template parameter:
error: invalid conversion from ‘void (*)(int)’ to ‘void (*)()’
This makes sense, because evaluating default arguments is a separate step in the invocation:
8.3.6.9: Default arguments will be evaluated each time the function is called.
The presence of default arguments does not alter the signature of your function. For example, you cannot use a single-argument function with a default argument to override a no-argument virtual member function.
Default argument values are not part of the function type. You can't use foo1 as a function taking no arguments, because it does take one argument. The argument gets filled in for you if you don't mention it, but it's still there.
Your workaround involving a dispatching function sounds like a good solution. It could even be templated if you need it a lot.
I'm pretty sure that a function pointer has signature of the function with all default parameters expanded and function pointers cannot convert to a function pointer with a different signature. Finding this in the standard is a different matter, though...
I think the relevant clause from the standard is 8.3.5 [dcl.fct] paragraph 6:
... The return type, the parameter-type-list, the ref-qualifier, and the cv-qualifier-seq, but not the default arguments (8.3.6) or the exception specification (15.4), are part of the function type. [ Note: Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions. —end note ]
Note that default arguments are the guys of the form = value according to 8.3.6 [dcl.fct.default] paragraph 1:
If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. ...
It won't compile because foo1 has signature:
void foo1(int a);
which you're trying to stick into a pointer to:
void F()
The function signatures don't match. The fact that foo1 has a default parameter doesn't change the function's signature (it still can take in an int).
A More Generic Solution
I'd say forget about the templates, they're only limiting you here.
Personally, the way I solve the callback problem is using function objects with argument binding. It can be done using the boost::function library, and binding default arguments with boost::bind (or std::bind1st and std::bind2nd).
These boost libraries are also built-in to the new C++11 standard, as std::function, and std::bind.
It's well worth taking a look at this, as it let's you do some very nice things, like providing default arguments to functions, or use class member functions as callbacks.
The sites I've linked to all have lots of example code, and the boost links have tutorials.

How to prevent Overloading?

Is it possible to prevent overloading of user defined functions in C++? Suppose I have a function as:
void foo(int , int);
Can I prevent foo from being overloaded, and if so how? If I can, can this be extended to prevent overriding of the methods through inheritance?
In a word: no. You can't prevent an overload of foo being defined with a different signature somewhere else and you also can't prevent virtual functions from being overriden.
In the C++ world you have to give some degree of trust to people writing code that winds up in your program.
Section 13.1.1 through 13.1.3 of the standard describe the kinds of functions that can not be overloaded:
Certain function declarations cannot be overloaded:
Function declarations that differ only in the return type cannot be overloaded.
Member function declarations with the same name and the same parameter types cannot be overloaded if any of them is a static member function declaration (9.4).
Note: as specified in 8.3.5, function declarations that have equivalent parameter declarations declare the same function and therefore cannot be overloaded:
Parameter declarations that differ only in the use of equivalent typedef “types” are equivalent. A typedef is not a separate type, but only a synonym for another type (7.1.3).
Parameter declarations that differ only in a pointer * versus an array [] are equivalent. That is, the array declaration is adjusted to become a pointer declaration (8.3.5). Only the second and subsequent array dimensions are significant in parameter types (8.3.4).
Parameter declarations that differ only in that one is a function type and the other is a pointer to the same function type are equivalent. That is, the function type is adjusted to become a pointer to function type (8.3.5).
Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called.
Two parameter declarations that differ only in their default arguments are equivalent.
Otherwise, the function can be overloaded, and there is no way to prevent that.
You cannot.
Make it a static function in a class and tell people not to modify that class?
This can be simulated by making it a struct instance:
struct foo_t {
void operator()(int, int);
} foo;
// error: redefinition of 'foo' as different kind of symbol
void foo(double, double);
or a function reference:
void foo_impl(int, int);
auto const& foo = foo_impl;
// error: redefinition of 'foo' as different kind of symbol
void foo(double, double);
Do what UncleBens said, and include in the class a constant which is an encrypted checksum of the text in the class, and a function to test it for validity. That way, nobody can change the class. Just a thought (maybe not a very good one).
As I think about it, that's pretty dumb because the class would need its own text. However, as an alternative, it could read its compiled binary code, and have a checksum for that. I'm getting out on a limb here.
The only reasonable way would be to give the function C linkage:
extern "C" void foo(int , int);
This works because with C linkage, names aren't mangled, so you can't do any overloading (which relies on encoding the types of arguments into the symbol name).
Obviously this won't extend to member functions.

Is the return type part of the function signature?

In C++, is the return type considered part of the function signature? and no overloading is allowed with just return type modified.
Normal functions do not include the return type in their signature.
(note: i've rewritten this answer, and the comments below don't apply to this revision - see the edit-history for details).
Introduction
However, the matter about functions and function declarations in the Standard is complicated. There are two layers that have to be considered:
Declarations
Entities
The so-called function declaration may declare a function entity or a template entity. If a function entity is declared, then you either have to do with an explicit specialization of a function template (with all arguments specified), or a declaration of an ordinary function. If a template entity is declared, then you are declaring a primary function template, or an explicit specialization where some arguments are not specified. (This is very similar to the relation of "object declaration" and objects or references: The former may declare either an object or a reference. So an object declaration may not necessarily declare an object!).
The Standard defines the signature of a function to include the following at 1.3.10:
The types of its parameters and, if the function is a class member, the cv- qualifiers (if any) on the function itself and the class in which the member function is declared. The signature of a function template specialization includes the types of its template arguments. (14.5.5.1)
It's missing the return type in this definition, which is part of the signature of a function template specialization (i.e a function declaration that declares a function which is a specialization of a template), as pointed out by 14.5.5.1 (recent C++0x working papers fixed that already to mention the return type in 1.3.10 too):
The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).
The signature of a function template consists of its function signature, its return type and its template parameter list.
So what exactly does a signature contain, again?
So, when we ask about the signature of a function, we have to give two answers:
For functions that are specializations of function templates, the signature includes the return type.
For functions that are not specializations, the return type is not part of the signature.
Notice, however, that the return type, in any case, is a significant part of the type of a function. That is, the following is not valid:
void f();
int (*pf)() = &f; // different types!
When is an overload invalid if only the return type differs?
Major compilers currently reject the following code:
int f();
double f(); // invalid
But accept the following code:
template<typename T> int f();
template<typename T> double f(); // invalid?
However, the Standard does forbid a function declaration that only differs in the return type (when defining when an overload is valid, and when not). It does not define precisely what "differs only by return type" means, though.
Standard paragraph references:
When can a function declaration be overloaded: 13.1
What is a function declaration: 7/2 and 7/5
What is the signature of a function template/specialization: 14.5.5.1
For reference, here is what the most recent C++0x draft n3000 says about "signature" in 1.3.11, which is much more complete in its coverage of the different type of entities:
the name and the parameter type list (8.3.5) of a function, as well as the class or namespace of which it is a member. If a function or function template is a class member its signature additionally includes the cv-qualifiers (if any) and the ref-qualifier (if any) on the function or function template itself. The signature of a function template additionally includes its return type and its template parameter list. The signature of a function template specialization includes the signature of the template of which it is a specialization and its template arguments (whether explicitly specified or deduced). [ Note: Signatures are used as a basis for name mangling and linking. — end note ]
It depends if the function is a function template or not.
In C++ Templates -- the complete guides, Jusuttis provides a different definition of that given in the C++ standard, but with equivalent consequences:
We define the signature of a function as the the following information:
The unqualified name of the function
The class or namespace scope of that name, and if the name has internal linkage, the translation unit in which the name is declared
The const, volatile, or const volatile qualification of the function
The types of the function parameters
its return type, if the function is generated from a function template
The template parameters and the template arguments, if the function is generated from a function template
As litb suggested, it's worth to clarify why the return type is part of the signature of a template function.
Functions can coexist in a program if
they have distinct signatures.
. That said, if the return type is a template parameter:
template <typename T>
T foo(int a)
{return T();}
it's possibile to instantiate two function which differ only in the return type:
foo<int>(0);
foo<char>(0);
Not only: as rightly reported by litb, it is also possible to overload two template functions, which differ only in the return type, even if the return type is not a dependent name. Here's his example:
template<class T> int foo(T)
{}
template<class T> bool foo(T)
{}
// at the instantiation point it is necessary to specify the cast
// in order not to face ambiguous overload
((int(*)(char))foo<char>)('a');
They are enough a part of the type that you can overload functions based on function pointer types that differ only by return type:
int IntFunc() { return 0; }
char CharFunc() { return 0; }
void FuncFunc(int(*func)()) { cout << "int\n"; }
void FuncFunc(char(*func)()) { cout << "char\n"; }
int main()
{
FuncFunc(&IntFunc); // calls void FuncFunc(int_func func)
FuncFunc(&CharFunc); // calls void FuncFunc(char_func func)
}
I find a useful way to implicitly make the return type part of a signature is to include a 'dummy' argument in the inputs.
For example:
template <typename T>
T f(double x, T dummy) {
T output;
output = x * 2;
return output;
}
In this case, if you want a double output you input:
f(2, double(1))
which returns the double 4.0, while if you want an int output you input:
f(2, int(1))
which returns the int 4.