Take a look at this seemingly innocent sample program:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; return 0; } int add(int x, int y) { return x + y; } |
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 the redundancy of the second error message, it’s useful to note that it is fairly common for a single error to produce (often redundant) multiple compiler 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 calls
One way to address the issue is to reorder the function calls so add is defined before main:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> int add(int x, int y) { return x + y; } int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; return 0; } |
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:
1 |
int add(int x, int y); // function prototype includes return type, name, parameters, and semicolon. No function body! |
Now, here’s our original program that didn’t compile, using a function prototype as a forward declaration for function add:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int add(int x, int y); // forward declaration of add() (using a function prototype) int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; // this works because we forward declared add() above return 0; } int add(int x, int y) // even though the body of add() isn't defined until here { return x + y; } |
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:
1 |
int add(int, int); // valid function prototype |
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 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:
1 2 3 4 5 6 7 8 9 10 11 |
#include <iostream> int add(int x, int y); // forward declaration of add() using function prototype int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; return 0; } // note: No definition for function add |
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:
Compiling... add.cpp Linking... add.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z) 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:
1 2 3 4 5 6 |
int add(int x, int y) // implements function add() { int z{ x + y }; // instantiates variable z return z; } |
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:
- Within a given file, a function, object, type, or template can only have one definition.
- 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).
- Within a given program, types, template functions, and inline functions can have multiple definitions so long as they are identical. 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 a compile to issue a redefinition error. Violating ODR parts 2 or 3 will cause the linker to issue a redefinition error. Here’s an example of a violation of part 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
int add(int x, int y) { return x + y; } int add(int x, int y) // violation of ODR, we've already defined function add { return x + y; } int main() { int x; int x; // violation of ODR, we've already defined x } |
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'
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:
1 2 |
int add(int x, int y); // tells the compiler about a function named "add" that takes two int parameters and returns an int. No body! int x; // tells the compiler about an integer variable named x |
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 both a definition and a declaration.
Quiz time
Question #4
Write the function prototype for this function (use the preferred form with names):
1 2 3 4 |
int doMath(int first, int second, int third, int fourth) { return first + second * third / fourth; } |
Question #5
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!
a)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int add(int x, int y); int main() { std::cout << "3 + 4 + 5 = " << add(3, 4, 5) << '\n'; return 0; } int add(int x, int y) { return x + y; } |
b)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int add(int x, int y); int main() { std::cout << "3 + 4 + 5 = " << add(3, 4, 5) << '\n'; return 0; } int add(int x, int y, int z) { return x + y + z; } |
c)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int add(int x, int y); int main() { std::cout << "3 + 4 + 5 = " << add(3, 4) << '\n'; return 0; } int add(int x, int y, int z) { return x + y + z; } |
d)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int add(int x, int y, int z); int main() { std::cout << "3 + 4 + 5 = " << add(3, 4, 5) << '\n'; return 0; } int add(int x, int y, int z) { return x + y + z; } |
![]() |
![]() |
![]() |
in solution one it says eturn instead of return
[ Fixed! Thanks. -Alex ]
So a function prototype is a kind of forward declaration?
It's more accurate to say that one use of function prototypes is for forward declarations. Function prototypes can also be used in header files and in class declarations (covered in chapter 8). So function prototypes have more uses than just as forward declarations.
i am experimenting the code from the quiz number 6 and i try to changed the forward declaration variables
from
to
after that i compiled the code by using microsoft visual studio 2008 and i notice that the code is successfuly compiled and the program is smoothly running.
why is it that the compiler is not complaining while the forward declaration variable is different from the implementation?
Good question! It turns out that the variable names in your forward declarations don't have to match those in the actual definitions. This is because the variable names in the forward declarations simply aren't used -- only the types are. You can even omit the variable names from the forward declaration altogether if you want.
However, I think it's generally good practice to put them in. If you see a forward declaration like this:
It's a lot less meaningful than if you see this:
now it's clear and i understand well.
thank you very much!
hi alex,
first of all thnks a lot for such nice tutorials.
i know its little too late to ask questions {looking at the postig dates}... however i have one doubt.
forward declaration in case of classes.
can i have forward declaration of a class [class A] in .cpp file. this class definition is present in other header fil [A.h].
now, in my code i can have a class [class B] containing pointer to this class and forward declaration [class A;]. this will get compiled and linked.
But if i create an object of class A in class B then i have to do #include "A.h" also.
Any specific reason for this, my thinking is that compiler would require complete definition of object getting created inside class B, whereas in case of pointer, well.. its just pointer.
m i thinking correct?
This helped explain a lot of things I was previously having problems with.
/* I have writen one another Program like example 5*/
#include <iostream.h>
#include <conio.h>
int add(int x, int y, int z);
void main()
{
int x,y,z;
clrscr();
cout << "n Enter three number : n";
cin >> x;
cin >> y;
cin >> z;
cout << "n" << x << " + " << y << " + " << z << " = " << add(x, y, z) << endl;
getch();
}
int add(int x, int y, int z)
{
return x + y + z;
}
Doesn't main have to be:
not
so that main returns a value?
Many (most?) compilers let you get away with using void main() instead of int main(), and they will implicitly return 0 when you do this. However, it's not technically part of the language, so I avoid it in my examples.
I tried to run your program and came up with and error like this
1>------ Build started: Project: 1, Configuration: Debug Win32 ------
1> 1.cpp
1>c:\users\marius\documents\visual studio 2010\projects\1\1\1.cpp(2): fatal error C1083: Cannot open include file: 'iostream.h': No such file or directory
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Since you use MSVS, you do not need to specify a library file, it takes care of that part for you so:
#include <stdafx.h>
#include <iostream>
Hi there. I was wondering how come I have to type #include stdafx.h now. I haven't used my compiler for a while, so I was just wondering. I can't do this program unless I do it, but the Hello World one works just fine without it.
By the way, in my older programs, the source files say "main.cpp" but the new one has 2 files, one that says stdfax.cpp and another one with the title of my project.
I'm new to programming, so if my questions sound dumb, bear with me.
stdafx.cpp is a file that Microsoft compilers use to do precompiled headers (which makes you program compile faster), if you sent them up correctly. If you don't want to deal with stdafx.h, you can always turn precompiled headers off (in the project settings).
Ahh, ok... I'm getting a clearer picture of this now (re: my question a few days ago in the previous 'functions' section
when I asked about the order of functions in Cpp code). This helps explain the reasons why it must be. :)
Thanks Alex!
-Dan
Yeah we had this problem back there too! :D
rying out your second example i wrote:
#include
int add(int x+int y)
{
return x+y;
}
int main()
{
using namespace std;
cout
Please repost your question with the code embedded inside PRE html tags.
If I am right... the arguments should not be divided by a positive or plus symbol but rather by a comma.
GovZ,
Forward declarations are used only to tell the compiler about the existence of a function (or class or variable) before it is actually implemented.
This information is only used during compile time. Consequently, forward declarations will not make your executables larger or slower.
Hello guys,
You have a great tutorial here. Worthy of publication, IMHO.
Anyways one question. Does Forward Declaration by function prototyping have an effect on the execution time or is this only used during the creation of some hash table or something? And that in the resulting executable, this step is not actually "re-read".
I hope I make sense. Thanks for your answers in advance. =)