Every function that a program uses must be defined exactly once in the program, except for inline functions. (Function templates are a little different; see Chapter 7 for details.) An inline function must be defined in every source file that uses the function. This section discusses function definitions and their relationship to their declarations.
In a source file, every function must be declared or defined
before it is used. For functions defined in libraries or other source
files, the convention is to declare the function in a header
(.h
or .hpp
) file, and the source file where the
function is called must #include
the header file. Every function that is used in the program must have
a definition.
Inline functions must be defined in every source file in which
they are used. This is typically accomplished by defining them in a
header file, which you must #include
in each source file that calls the
inline function. Every definition of an inline function must be
identical.
A function type includes the language linkage, return type, parameter types, and cv-qualifiers. Note that for each parameter, only its type is significant; its name, storage class, and cv-qualifiers are not part of the function type. Exception specifications are not part of a function's type.
A single source file can have multiple declarations of the same
function (that is, functions with the same type), even if those
declarations differ in other ways. For example, one declaration can
declare a parameter const
and
another might declare it volatile
.
Because cv-qualifiers on parameters do not affect
a function's type, both declarations are equivalent. (Parameter
qualifiers matter only in the function definition, not the
declaration.) Example 5-7
shows several declarations and one definition, all for a single
function.
Example 5-7. Declaring and defining functions
// Three declarations of the same function type int add(const int a, const int b); int add(int x, volatile int); int add(signed int, int signed); // Definition of the function. The parameter qualifiers in the declarations are // irrelevant. Only those in the definition matter. int add(int x, int y) { return x + y; }
Array and pointer parameter types are also equivalent. In a
function body, a parameter with an array type is actually a pointer,
not an array. The first (leftmost) size in a multidimensional array is
ignored and can be omitted. Similarly, a parameter of function type is
the same as a parameter of function pointer type. The rules for
function types apply recursively to function and function pointer
parameters. Thus, a parameter with a type that is a pointer to a
function that takes an int
is
equivalent to one with a type that is a pointer to a function that
takes a const
int
parameter. Example 5-8 illustrates equivalent
pointer types.
Example 5-8. Equivalent pointer types
int first(int const array[10]); // Size is ignored int first(int const array[]); // Equivalent declaration int first(int const *array); // Equivalent declaration int first(int const *array) // Definition { return array[0]; } int apply(int func(int), int arg); int apply(int func(int const), int); // Equivalent int apply(int (*func)(int), int arg); // Equivalent int apply(int (*func)(int const), int arg) // Definition { return func(arg); }
Because typedef
declarations do not create new types, but only synonyms
for existing types, parameters that differ only in their use of
typedef
types are equivalent, as
shown in Example 5-9.
Example 5-9. Using typedef in equivalent parameter types
typedef int INT; typedef int const CINT; void func(int); void func(INT); // Equivalent void func(INT const); // Equivalent void func(CINT); // Equivalent void func(signed int i) // Definition { std::cout << i; }
You can declare a function type from a typedef
, but you cannot use the typedef
in a function definition. This usage
is uncommon. For example:
typedef int func(int, int); func add; // Declares int add(int, int); int add(int a, int b) // Cannot use "func add" here { return a + b; }
Be careful to distinguish between a function type and a function pointer type. A function
can be implicitly converted to a function pointer and can be called
using a function pointer. A function pointer, however, cannot be used
to declare a function. The following is an example of the implicit
conversion of a function, add
, to a
function pointer, and the use of a function pointer, a
, to call the function:
typedef func* funcptr; // Pointer-to-function type funcptr a = add; // Pointer-to-function object int i = a(1, 2); // Call the function that a points to.
A function signature is similar to a function type, but it ignores the return type. Overload resolution relies on function signatures to determine which overloaded function to call. See Section 5.3 later in this chapter.
A function definition consists of a function declaration followed by a function body. The function body has one of two forms:
compound-statement
Executed when the function is called. When execution
reaches a return
statement or
the end of the compound statement, the function returns.
try
ctor-initializers
compound-statement
handlers
Sets up a try
block
that surrounds the constructor initializers and function body.
If an exception is thrown from any of the
ctor-initializers
, it is handled by
the handlers in the same manner as any exception thrown from the
compound statement. Thus, this form is typically used only with
constructors that have initializers. See Chapter 6 for more information
about constructor initializers.
The constructor initializers in the second form are optional.
Without them, the form can be used for any function. It is equivalent
to having a try
statement around
the entire function body. Example
5-10 shows a try
function
body used in this way.