KidneyChris
KidneyChris

Reputation: 857

How do I explain this LNK2005?

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

Answers (3)

Joseph Mansfield
Joseph Mansfield

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

Alex F
Alex F

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

Luchian Grigore
Luchian Grigore

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

Related Questions