2.6 — Forward declarations and definitions

Take a look at this seemingly innocent sample program:

You would expect this program to produce the result:

The sum of 3 and 4 is: 7

But in fact, it doesn’t compile at all! Visual Studio produces the following compile error:

add.cpp(5) : error C3861: 'add': identifier not found

The reason this program doesn’t compile is because the compiler compiles the contents of code files sequentially. When the compiler reaches the function call to add on line 5 of main, it doesn’t know what add is, because we haven’t defined add until line 9! That produces the error, identifier not found.

Older versions of Visual Studio would produce an additional error:

add.cpp(9) : error C2365: 'add' : redefinition; previous definition was 'formerly unknown identifier'

This is somewhat misleading, given that add wasn’t ever defined in the first place. Despite this, it’s useful to generally note that it is fairly common for a single error to produce many redundant or related errors or warnings.

Best practice

When addressing compile errors in your programs, always resolve the first error produced first and then compile again.

To fix this problem, we need to address the fact that the compiler doesn’t know what add is. There are two common ways to address the issue.

Option 1: Reorder the function definitions

One way to address the issue is to reorder the function definitions so add is defined before main:

That way, by the time main calls add, the compiler will already know what add is. Because this is such a simple program, this change is relatively easy to do. However, in a larger program, it can be tedious trying to figure out which functions call which other functions (and in what order) so they can be declared sequentially.

Furthermore, this option is not always possible. Let’s say we’re writing a program that has two functions A and B. If function A calls function B, and function B calls function A, then there’s no way to order the functions in a way that will make the compiler happy. If you define A first, the compiler will complain it doesn’t know what B is. If you define B first, the compiler will complain that it doesn’t know what A is.

Option 2: Use a forward declaration

We can also fix this by using a forward declaration.

A forward declaration allows us to tell the compiler about the existence of an identifier before actually defining the identifier.

In the case of functions, this allows us to tell the compiler about the existence of a function before we define the function’s body. This way, when the compiler encounters a call to the function, it’ll understand that we’re making a function call, and can check to ensure we’re calling the function correctly, even if it doesn’t yet know how or where the function is defined.

To write a forward declaration for a function, we use a declaration statement called a function prototype. The function prototype consists of the function’s return type, name, parameters, but no function body (the curly braces and everything in between them), terminated with a semicolon.

Here’s a function prototype for the add function:

Now, here’s our original program that didn’t compile, using a function prototype as a forward declaration for function add:

Now when the compiler reaches the call to add in main, it will know what add looks like (a function that takes two integer parameters and returns an integer), and it won’t complain.

It is worth noting that function prototypes do not need to specify the names of the parameters. In the above code, you can also forward declare your function like this:

However, we prefer to name our parameters (using the same names as the actual function), because it allows you to understand what the function parameters are just by looking at the prototype. Otherwise, you’ll have to locate the function definition.

Best practice

When defining function prototypes, keep the parameter names. You can easily create forward declarations by using copy/paste on your function declaration. Don’t forget the semicolon on the end.

Forgetting the function body

New programmers often wonder what happens if they forward declare a function but do not define it.

The answer is: it depends. If a forward declaration is made, but the function is never called, the program will compile and run fine. However, if a forward declaration is made and the function is called, but the program never defines the function, the program will compile okay, but the linker will complain that it can’t resolve the function call.

Consider the following program:

In this program, we forward declare add, and we call add, but we never define add anywhere. When we try and compile this program, Visual Studio produces the following message:

add.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" ([email protected]@[email protected])
add.exe : fatal error LNK1120: 1 unresolved externals

As you can see, the program compiled okay, but it failed at the link stage because int add(int, int) was never defined.

Other types of forward declarations

Forward declarations are most often used with functions. However, forward declarations can also be used with other identifiers in C++, such as variables and user-defined types. Variables and user-defined types have a different syntax for forward declaration, so we’ll cover these in future lessons.

Declarations vs. definitions

In C++, you’ll often hear the words “declaration” and “definition” used, often interchangeably. What do they mean? You now have enough of a framework to understand the difference between the two.

A definition actually implements (for functions or types) or instantiates (for variables) the identifier. Here are some examples of definitions:

A definition is needed to satisfy the linker. If you use an identifier without providing a definition, the linker will error.

The one definition rule (or ODR for short) is a well-known rule in C++. The ODR has three parts:

  1. Within a given file, a function, object, type, or template can only have one definition.
  2. Within a given program, an object or normal function can only have one definition. This distinction is made because programs can have more than one file (we’ll cover this in the next lesson).
  3. Types, templates, inline functions, and variables are allowed to have identical definitions in different files. We haven’t covered what most of these things are yet, so don’t worry about this for now -- we’ll bring it back up when it’s relevant.

Violating part 1 of the ODR will cause the compiler to issue a redefinition error. Violating ODR part 2 will likely cause the linker to issue a redefinition error. Violating ODR part 3 will cause undefined behavior.

Here’s an example of a violation of part 1:

Because the above program violates ODR part 1, this causes the Visual Studio compiler to issue the following compile errors:

project3.cpp(9): error C2084: function 'int add(int,int)' already has a body
project3.cpp(3): note: see previous definition of 'add'
project3.cpp(16): error C2086: 'int x': redefinition
project3.cpp(15): note: see declaration of 'x'

For advanced readers

Functions that share an identifier but have different parameters are considered to be distinct functions. We discuss this further in lesson 10.7 -- Function overloading

A declaration is a statement that tells the compiler about the existence of an identifier and its type information. Here are some examples of declarations:

A declaration is all that is needed to satisfy the compiler. This is why we can use a forward declaration to tell the compiler about an identifier that isn’t actually defined until later.

In C++, all definitions also serve as declarations. This is why int x appears in our examples for both definitions and declarations. Since int x is a definition, it’s a declaration too. In most cases, a definition serves our purposes, as it satisfies both the compiler and linker. We only need to provide an explicit declaration when we want to use an identifier before it has been defined.

While it is true that all definitions are declarations, the converse is not true: all declarations are not definitions. An example of this is the function prototype -- it satisfies the compiler, but not the linker. These declarations that aren’t definitions are called pure declarations. Other types of pure declarations include forward declarations for variables and type declarations (you will encounter these in future lessons, no need to worry about them now).

The ODR doesn’t apply to pure declarations (it’s the one definition rule, not the one declaration rule), so you can have as many pure declarations for an identifier as you desire (although having more than one is redundant).

Author's note

In common language, the term “declaration” is typically used to mean “a pure declaration”, and “definition” is used to mean “a definition that also serves as a declaration”. Thus, we’d typically call int x; a definition, even though it is both a definition and a declaration.

Quiz time

Question #1

What is a function prototype?

Show Solution

Question #2

What is a forward declaration?

Show Solution

Question #3

How do we declare a forward declaration for functions?

Show Solution

Question #4

Write the function prototype for this function (use the preferred form with names):

Show Solution

Question #5

For each of the following programs, state whether they fail to compile, fail to link, fail both, or compile and link successfully. If you are not sure, try compiling them!


Show Solution


Show Solution


Show Solution


Show Solution

2.7 -- Programs with multiple code files
2.5 -- Why functions are useful, and how to use them effectively

323 comments to 2.6 — Forward declarations and definitions

  • José

    Isn't the option 1 the best out the two? When should I use the option 2?

  • Tim

    I'm having trouble with forward declarations. I've whittled the code down to where the error is, but can't see it (the second block of code is input.cpp). The error msg is:

           Input.cpp(5,1): error C2447: '{': missing function header (old-style formal list?)

    Can you see what's wrong, please?

    • Axel

      You have an extra(incorrectly placed) ; on line 12. You are also including iostream twice, but your issue is that closed statement on line 12. Basically the compiler is complaining that your curly brackets don't have a function to go with them.

    • rzrzl

      Your fixed code with some explanations

  • Taras

    That was the coolest explanation since I dived into C++! Thx!

  • Nathan

    Question 5 states:

    "For each of the following programs, state whether they fail to compile, fail to link, or compile and link. If you are not sure, try compiling them!"

    But the answer to question 5 d is:

    "Compiles and links. The function call to add() matches the prototype that was forward declared, the implemented function also matches."

    "Compiles and links" is not one of the possible answers in the question. I figured it would compile, but if it had been more complicated I might have spent a long time trying to figure out what was wrong with it, before finally giving up and realizing that nothing was wrong.

    Sorry if it seems minute, but yea. It's kind of like a buggy game - you start wondering whether you missed something, or if it's simply a bug.

    • nascardriver

      I updated the question to make it easier to understand that all combinations are possible. Thanks for expressing your concern!

  • Lord Voldemort

    Don't you think it is redundant to have identical definition in different files as mentioned in ODR.

    • Alex

      Identical definitions of what?

      Remember that the compiler compiles file-by-file. This means each file must include all of the declarations and definitions required for that file to compile. In some cases, (e.g. for variables) declarations are enough to use them in multiple files. But for other things, full definitions are needed. For example, with program-defined types (types that you've created for use in your own programs, e.g. enums, structs, classes, etc...), it would be impossible to use those types in more than one file if type definitions were limited to a single file.

  • arntj

    Seems like there is an unclosed <i> tag on this page. Even this comment is in italic...

  • koe

    I think this chapter could use some added clarity, since multiple functions with the same identifier but different parameters can work.

    • nascardriver


      could you explain where exactly you think this chapter needs clarification?
      Function overloads (Multiple functions with the same name) are covered later.

      • koe

        "A definition actually implements (for functions or types) or instantiates (for variables) the identifier."

        This isn't entirely accurate, since a function identifier is sub-related to its parameters. If just the identifier is relevant to a definition the ODR would seem to preclude function overloads.

        Possible improvement:

        "A definition actually implements (for functions or types) or instantiates (for variables) an identifier with its related details (type, parameters, etc.)."

  • Humam

    How would you say that int x; is a definition?

    int x; has not been initialized, if I did std::cout << x; it will give me a compiler error because we are using uninitialized memory x (and using uninitialized local variable x)

    Wouldn't the definition of x make more sense if it was initialized (Given a value) at the same time? Instead I'm looking at int x; more as a forward declaration for a variable, even though you stated that forward declarations of variables require a different syntax (Supposedly not the same as int x;)

    I hope you understand my point, this downright confused me right now, maybe I need missed some simple point, clarification would be great, thanks.

    • nascardriver

      `int x;` is a definition. `x` exists, it has a location in memory, the value doesn't matter. Things can be defined without being initialized.
      A declaration only declares that something exists, but it doesn't create anything.

      You can think of a declaration as me saying "I have a bucket". You now know that I have a bucket. It might exist, but I might have lied to you.
      A definition is me showing you how I create the bucket. You no longer need an explicit declaration, because it's obvious that I have a bucket, you saw it.
      In neither of these cases do you know what's inside of my bucket. You only know what's inside if you try to find and use my bucket after I declared it, or you saw how I defined it and I poured something into it (I initialized it). If I didn't fill it with anything, it exists anyway.

  • BooGDaaN

    Hello! I created this code:

    I don't understand why when i call function:

    In console firstly appears to introduce the multiplicator value, not the integer value.

    • 5irKawfycups

      See this from Lesson 2.3:

      "A warning about function argument order of evaluation

      The C++ specification does not define whether arguments are matched with parameters in left to right order or right to left order. When copying values, is of no consequence. However, if the arguments are function calls, then this can be problematic"

      In your case, I would separate out the function to take the integer input from user and the function to take the multiplicator from user.

  • Jonas

    Found an error under the part about the 'one definition rule' (error marked with asterisks):

    Violating part 1 of the ODR will cause *a compile* to issue a redefinition error. Violating ODR parts 2 or 3 will cause the linker to issue a redefinition error.

    I assume this should say 'the compiler' instead of 'a compile'.

  • Nikola

    Hello Alex,

    Just a side note. In the section "Declaration vs. definition", line three (of the text) is written as "A definition is needed to satisfy the linker. If you use an identifier without providing a definition, the linker will error."
    I know what you wanted to say but the ending of the second sentence is wrong, I think - ", the linker will throw an (perhaps) error."


  • alfonso

    b) Doesn’t compile. The compiler will complain that the add() called in main() does not have the same number of parameters as the one that was forward declared.

    Is what will say true?
    The compiler gives us more information telling we have a declared function with the same identifier but less parameters. But in fact, the program won't compile because the add function used in main (with three parameters) was not forward declared. Because we can have functions with the same identifier but different types or number of parameters. add function with three parameters is not the same function as add function with two parameters.

    • Alex

      Correct, so the compiler will complain that the user is trying to call an add() function with 3 parameters but it can't find a declaration for such a function.

  • alfonso

    Which is the difference between an instantiated variable and an initialized variable?

  • Eva

    Hey Alex,

    why is it necessary to state the type of the function again if we are defining the function body, when we have already given the type in the forward declaration?
    Since I cannot define two functions with the same name but different types anyway this seems unnecessary. But the compiler complains if I do this saying that I can have no declaration without a type, which would be fine...but I thought we declared the function in the line above main() and are now simply giving the definition...
    Also, if I would deal with variables and I defined variable int x without initialising it and wanted to initialise it later on I would simply say x = 5
    without explicitly providing the type of the variable, because it is already known that x is an integer.  Why does this not work for functions? I think I am missing something here...

    • Alex

      What do you mean by "type of the function"?

      The purpose of the forward declaration is to provide the compiler with a way to pre-check whether a call to the function is typed correctly before seeing the actual definition of the function.

      It's not redundant to include return type and parameter information in both the forward declaration and the definition. When the compiler compiles, it does so file by file. The function definition may serve as the declaration in the file the function is defined in, whereas in another file, the forward declaration is used. Omitting information from either would not give the compiler enough information to do type checking.

      I think this will make more sense once you've gotten through the lessons on multiple files.

      • Eva

        Thank you so much for your reply!

        I have gone through the lessons on multiple files, but it still, kind of, does not make sense to me...maybe because I am missing an example where things could go wrong if it were any different.

        I do get that if I have my main.cpp program (with main()) and I am using a function, say int add(int x, int y), that is defined in an add.cpp and has a forward declaration in main.cpp I have to provide the return type of the function both in main.cpp and add.cpp because both files will be compiled separately and the compiler needs to know the return type of add(). (The definition of add() in add.cpp serves also as the declaration of the function in this file.) That is completely logical to me.

        What I do not understand is the following case:
        Let's say I write a header file add.h where I declare function int add(int x, int y). I include add.h in the add.cpp and main.cpp (where I have now deleted the forward declaration of add() ). Now, when compiling add.cpp the compiler will first see the declaration of add() from the header file add.h and then the definition of add() in the add.cpp. In the declaration I have already stated that the return type of add() is integer. I cannot define another function with the same identifier but a different return type (in this file) because the compiler would throw an error.
        So why do I need to provide the return type for add() in the definition in this case? Is this 'just' a fail safe? Or why can't the compiler match the function add() from the add.cpp with the one in add.h?

        • Alex

          I'm not sure there's any particular reason. But a couple of speculations:
          1) Definitions act as declarations, and it would be weird to have a definition that was missing information
          2) Having each declaration be a full declaration helps prevent code from breaking when modified (e.g. if you no longer included that header)

  • blazk

    this code has two definition with same name but different arity. It compiles fine.
    is two function with same name but different arity considered as different function?

    and I tried using both function

    and it runs fine as expected. so it seems like same name function with different arity is treated like different function

  • Juan

    Thank you very much for explaining the difference in an understandable way. I looked for it in Stack Overflow but those who "explained" gave few truths and a lot of theater. Thanks!

  • Levi

    "New programmers often wonder what happens if forward declare a function but do not define it." idk if this is complete english haha

  • Ethan Smith

    What is the standard for placing in Forward declarations, should they be outside of any functions and at the start of the code outside of functions entirely?

  • Derick

    I find the section the one definition rule a bit confusing.

    The first part states: "Within a given file, an identifier can only have one definition.", but I can have two variables with the same name in the same file, as long as they're in different scopes.

    I guess the question is, what constitutes an identifier?

    • Alex

      I updated the section on the ODR to make it more accurate, as you are correct -- it's not the identifier that must be unique, it's the underlying things themselves (objects, functions, types, etc...) that must be unique.

      An identifier is just a name for something, and as you correctly note, identifiers don't need to be unique so long as they're in different scopes.

  • Ove

    In the section Function prototypes and forward declaration of functions you write this tip:
    Tip: You can easily create function prototypes by using copy/paste on your function declaration. Don’t forget the semicolon on the end.

    My understanding is that you meant to write the following:
    Tip: You can easily create function prototypes by using copy/paste on your function definition. Don’t forget the semicolon on the end.

    Is this correct or have I misunderstood something?
    Thanks for these awesome tutorials!

    • The declaration is the top of the function

      The definition includes the function body (the part inside the curly brackets)

      • Ove

        Aha! Ok, thanks.
        My understanding was that the function prototype and forward declaration was the same thing and that the actual function is the definition.

        Have I understood it rightly that there are two kinds of declarations?
        Forward declaration where you have the function prototype, and function declaration where you have the definition.

        • > My understanding was that the function prototype and forward declaration was the same thing and that the actual function is the definition.
          That's correct.

          > Have I understood it rightly that there are two kinds of declarations?
          You can either have the declaration alone (forward declaration/prototype) or the declaration along with the definition. I wouldn't call that two kinds of declaration since it's the same thing. Only what comes after the declaration is different (semicolon or body).

  • Aditya

    Hey Alex!
    Thanks for the correction.
    Now, I want this code to calculate in decimals. That is,if i say 35/45,i want  to get the answer in decimals.
    How do i do that?

    • Alex

      If you use type "double" instead of "int", you can use numbers with decimals (e.g. 1.45). Handling fractions (e.g. 35/45) is a lot more complicated and far beyond the scope of these introductory lessons.

      I do show examples of a Fraction class in chapter 8 or 9.

  • aditya

    What is wrong with this code?

  • Diego Sandoval

    You say that a function can have only one definition, yet this code compiles and executes correctly. both functions are called (depending on the arguments provided). I didn't understand the rule correctly? right?

    • nascardriver

      Hi Diego!

      Those are two different functions which share the same name (Function overloading).
      The one definition rule is only valid for the same function header (Same return-type, same name, same parameters).

      * Lesson 7.6 - Function overloading

  • Hi, couldn't all the forward definitions just be put together in a header file such as <<forward.h>> and precompiled with a #include call at the top such as


    • nascardriver

      Hi Nigel!

      Yes, that's how it's usually done, because it allows usage of the functions from any file. Nothing is being compiled though, only the contents will be copied.

      • thanks, just getting my head around why we were looking at "global" scoped definitions in the program that just make the listing look untidy. Are headers covered later?

        • nascardriver

          Yep, starting with the next lesson.
          The code on learncpp is single-file in most cases, because it's easier to understand that way. In your projects, you should use multiple files.

  • jft

    This is always a definition as a memory location is allocated for x (which is uninitialized).

    This is a declaration as no memory is allocated for x and x here refers to x defined in another translation unit.

  • Zane

    Why does this work?

    I ask, because the definition of add() is after that of the body of main(). If the the program is read/ran sequentially, when would it ever encounter the definition? Shouldn't the definition come before main()? If not, I am missing something. As I understand it, at the return of a value for main() the program stops, never having made it to/read add().

    • nascardriver

      Hi Zane!

      Your compiler compiles all functions, no matter where they are. To call a function, all your compiler needs to know, is the function declaration, not the definition. After compilation the order of functions doesn't matter, because your computer can call functions above and below the calling function.

  • Deepesh Choudhary

    Okay, so a declaration is used just to satisfy the compiler.
    Does that mean that the declaration statement does not get converted to any machine code?
    Does that also mean that if main.o is reversed, there won't be any code/statements indicating such declaration?

    • nascardriver

      Hi Deepesh!

      Yes to both.
      The compiler will generate the function at address Y. Your computer isn't restricted to calling functions that have been declared before the current point of execution. It can just from address X to address Y without worries.

  • Pork and Beans

    Hello Alex!

    I hope you are gearing up for a stellar weekend.

    I also hope that you do, in fact, want me to point out any mistakes I see.

    In the paragraph above "Quiz", there is a mistake.

    "Others types of pure declarations..." should be "Other".

    I thought you would want to know.

    Thank you as always for your incredible work.

    Have a nice day.

  • Farzad.S

    I see, by Forward declarations and definitions, we can call the functions in any order we want. Am I right?

  • Farzad.S

    Hello, I did understand the Forward declarations and prototype. But I am curious why do we need it, we can just use function and keep calling it. I believe it makes the program more complicated.

    By the way, for ages, I was trying to learn C++ but couldn't find the right book or courses to clearly teaching me from scratch. Great job. thanks a lot

    • nascardriver

      Hi Farzad!

      > I believe it makes the program more complicated
      It sure does, but there are situations where it cannot be avoided.

      This program will just run an infinite loop, but there are cases in which a construction like this is required.

    • vbot

      My preferred programing-flow is linear from top to bottom, because I'm used to languages where the order of function definitions and calls doesn't matter:

      so this is where I see the need for forward declarations.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]