Reputation: 857
So, someone came to me with a project that failed linking with the error LNK2005: symbol already defined in object (using Visual Studio 2010). In this case, I know what is wrong (and hence could point them to the correct solution), but I don't know why this is wrong on a level to give a good explanation about it (to prevent it happening again).
// something.h
#ifndef _SOMETHING_H
#define _SOMETHING_H
int myCoolFunction();
int myAwesomeFunction() // Note implementing function in header
{
return 3;
}
#endif
-
// something.cpp
#include "something.h"
int myCoolFunction()
{
return 4;
}
-
// main.cpp
#include <iostream>
#include "something.h"
int main()
{
std::cout << myAwesomeFunction() << std::endl;
}
This fails linking, and is fixed by putting myAwesomeFunction() into the .cpp and leaving a declaration in the .h.
My understanding of how the linker works comes pretty much from here. To my understanding, we are providing a symbol that is required in one place.
I looked up the MSDN article on LNK2005, which matches how I expect linkers to behave (provide a symbol more than once -> linker is confused), but doesn't seem to cover this case (which means I'm not understanding something obvious about linking).
Google and StackOverflow yield issues with people not including an #ifndef
or #pragma once
(which leads to multiple declarations of provided symbols)
A related question I found on this site has the same problem, but the answer doesn't explain why we're getting this problem adequately to my level of understanding.
I have a problem, I know the solution, but I don't know why my solution works
Upvotes: 4
Views: 170
Reputation: 110658
In a typical C++ project, you compile each of the implementation (or .cpp
) files separately - you generally never pass a header (or .h
) file to the compiler directly. After all preprocessing and inclusions are performed, each of these files becomes a translation unit. So in the example you've given, there are two translation units that look like this:
main.cpp
translation unit:
// Contents of <iostream> header here
int myCoolFunction();
int myAwesomeFunction() // Note implementing function in header
{
return 3;
}
int main()
{
std::cout << myAwesomeFunction() << std::endl;
}
something.cpp
translation unit:
int myCoolFunction();
int myAwesomeFunction() // Note implementing function in header
{
return 3;
}
int myCoolFunction()
{
return 4;
}
Notice that both of these translation units contain duplicate content because they both included something.h
. As you can see, only one of the above translation units contains a definition of myCoolFunction
. That's good! However, they both contain a definition of myAwesomeFunction
. That's bad!
After the translation units are compiled separately, they are then linked to form the final program. There are certain rules about multiple declarations across translation units. One of those rules is (§3.2/4):
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.
You have more than one definition of myAwesomeFunction
across your program and so you are breaking the rules. That's why your code doesn't link correctly.
You can think of it from the linker's perspective. After these two translation units are compiled, you have two object files. The linker's job is to connect the object files together to form the final executable. So it sees the call to myAwesomeFunction
in main
and tries to find a corresponding function definition in one of the object files. However, there are two definitions. The linker doesn't know which one to use so it just gives up.
Now let's see what the translation units look like if you define myAwesomeFunction
in something.cpp
:
Fixed main.cpp
translation unit:
// Contents of <iostream> header here
int myCoolFunction();
int myAwesomeFunction();
int main()
{
std::cout << myAwesomeFunction() << std::endl;
}
Fixed something.cpp
translation unit:
int myCoolFunction();
int myAwesomeFunction();
int myCoolFunction()
{
return 4;
}
int myAwesomeFunction()
{
return 3;
}
Now it's perfect. There is only one definition of myAwesomeFunction
across the whole program now. When the linker sees the call to myAwesomeFunction
in main
, it knows exactly which function definition it should link it to.
Upvotes: 3
Reputation: 43311
myAwesomeFunction
is defined in two source files: something.cpp
and main.cpp
. Move its implementation to one of source files, or declare this function as static.
Upvotes: -2
Reputation: 258608
The linker is merely letting you know that you broke the one definition rule. This is a basic, well-documented rule of C++ - it isn't solved by using include guards or #pragma once
directives, but, in case of a free function, by marking it inline
or moving the implementation to a source file.
When a non-inline method is implemented in a header, all translation units that include that header will define it. When the corresponding .obj
files are linked together, the linker detects the same symbol is exported (and defined) multiple times, and complains.
Moving the implementation to a cpp
file effectively transforms your initial definition into a declaration.
Upvotes: 1