5.2 Function Definitions

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.

5.2.1 Declarations and Definitions

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.

5.2.2 Function Types and Signatures

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.

5.2.3 Function Bodies and try Blocks

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.

Example 5-10. A try function body
void func1(  )

try {

  // Statements

} catch (...) {

  // Handler

}



void func2(  )

{

  try {

    // Statements

  } catch(...) {

    // Handler

  }

}