Functions

Function Declarations and Definitions

A function needs to be known to the compiler before it can be used. In C++, there are two ways for the compiler to learn about a function: through a declaration or through a definition.

A declaration tells the compiler the name of the function, what value the function is guaranteed to return, and the types of arguments the function expects to receive. A declaration acts as a promise that a definition will be seen somewhere.

A definition is the implementation of the function; it is the actual code that makes the function do work.

Every definition is also a declaration, but not every declaration is a definition.

The compiler reads a source file from top to bottom, and it tries to compile code as it sees it.

When a call to a function is encountered that the compiler doesn't know about yet, it doesn't know what arguments the function is expecting to be given, and the compiler doesn't know what kind of value (if any) that function is expected to return.

This results in a compiler error.

#include <iostream>

int main()
{
    std::cout << "The sum is " << add(3, 4) << '\n';
}

int add(int x, int y)
{
    // Return the computed value to the caller
    return x + y;
}

Putting the entire function definition before the call makes the problem go away, because putting the definition in the source file introduces the function to the compiler.

This is fine for very small programs, but it may start to become irritating as the program grows larger, because you may find that you have had to place functions in unnatural orders so that the definition is always seen before the call to that function.

The standard practice is instead to use a forward declaration, also called a function prototype.

A forward declaration looks exactly like a function definition minus the body and ends with a semicolon.

#include <iostream>

// Declare add before code needs to call it
int add(int x, int y);

int main()
{
    std::cout << "The sum is " << add(3, 4) << '\n';
}

int add(int x, int y)
{
    return x + y;
}

A forward declaration makes a promise to the compiler about a function with a specific return type, with a specific name, and a specific list of parameters. After seeing a forward declaration, calls to that function can be checked against that declaration.

The compiler does not know yet how to implement that function, but it does know that a call to that function must look a certain way. At some point, the definition for that function must appear, and that definition must match the forward declaration.

The parameters do not need names within a function declaration, because the only type information that the compiler requires from a declaration is that a function takes so many arguments of specific types. When the definition of the function is a large distance away from the forward declaration of the function, having parameter names in the forward declaration can be beneficial.

int add(int, int);

The names themselves may or may not be identical to the parameter names in the definition. When separated by a long gap of code, explicit parameter names can help to remind a person reading the code what the meaning and purpose of each variable passed into the function is supposed to be.

This rule can be followed strictly, but is not strictly necessary.

int add(int first, int second);

int add(int x, int y)
{
    return x + y;
}

A program can have as many declarations of one function as it likes. However, a function can have only one definition. This is covered under the One Definition Rule, and providing two or more definitions will result in an error at the linking stage.

Multiple declarations are fine because they are all describing the same function: they are providing the compiler with the same information as each other.

Two definitions would cause trouble because the program would contain more than one definition of that specific function.

int add(int x, int y);
int add(int a, int b);

int add(int x, int y)
{
    return x + y;
}

If the function declaration is compared to the function definition, and the parameters and return types are different, then these are two separate functions, not one. It is possible for a function call to be checked against the declaration, and for it not to fail at compile time, but then fail at linking time because a function definition with that specific signature may not be present in the entire program.

If one is declared to take in a double, but is defined as taking in an int, then these are not the same function.

They just happen to have the same name.

int process(double value);

int process(int value)
{
    return value * 2;
}

int main()
{
    process(3.14);
}

The simple way to check them would be to compare their return and parameter types.

So, you have determined that a function declaration is the best way of letting your compiler know about any function that it may not see until later. It is the compiler that benefits from knowing this - the function call does not need to be preceded by the definition of the call itself.

This makes writing the code simpler, because you do not have to worry about writing the definitions first, then the callers, then the returners.

All you need is to write out your declaration so that it gives your compiler enough information to make sense of any calls you will make to this function.

The definition should then appear once in the overall program's code.

Exercise: Function Declarations and DefinitionsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Function Declarations and Definitions

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Add an add(8, 9) call in main and print the result. Keep the existing declaration before main and definition after main.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

// Declare add before code needs to call it
int add(int x, int y);

int main()
{
    std::cout << "The sum is " << add(3, 4) << '\n';
}

int add(int x, int y)
{
    return x + y;
}

Hints

Hint 1

The declaration before main tells the compiler how to check add(3, 4). The matching definition after main supplies the return x + y statement.

Hint 2

Add another call inside main without moving the definition. Check that its argument types and count still match the earlier declaration.

Worked solution

The added add(8, 9) call returns 17. The declaration before main permits the call to be checked, and the existing definition after main supplies its implementation.

#include <iostream>

// Declare add before code needs to call it
int add(int x, int y);

int main()
{
    std::cout << "The sum is " << add(3, 4) << '\n';
    std::cout << "Another sum is " << add(8, 9) << '\n';
}

int add(int x, int y)
{
    return x + y;
}

Example output

The sum is 7
Another sum is 17

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.