How to explain this LNK2005?

So, someone came to me with a project that could not associate with the LNK2005 error: a character already defined in the object (using Visual Studio 2010). In this case, I know what is wrong (and therefore may indicate the correct solution), but I do not know why it is wrong at the level to give a good explanation for this (so that it does not happen 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 is not binding related and is fixed by placing myAwesomeFunction () in .cpp and leaving the declaration in .h.

My understanding of how the linker works depends a lot on here . As I understand it, we provide the symbol that is required in one place.

I looked at an article that matches the way I expect linkers to behave (provide the character more than once β†’ the linker is confused), but does not seem to cover this case (which means that I don't understand anything obvious about the links) .

Google and StackOverflow are having problems with people, not including #ifndef or #pragma once (which leads to multiple declarations of the provided characters)

The related question that I found on this site has the same problem, but the answer does not explain why we adequately evaluate this problem with my level of understanding.

I have a problem, I know the solution, but I do not know why my solution works

+4
source share
3 answers

In a typical C ++ project, you compile each of the implementation files (or .cpp ) separately - you never pass the header file (or .h ) to the compiler directly. After pre-processing and inclusion, each of these files becomes a translation unit. So, in the example that you specified, 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; } 

Note that both of these translation units contain duplicate content because they both included something.h . As you can see, only one of the listed translation units contains the definition of myCoolFunction . It's good! However, both of them contain a definition of myAwesomeFunction . This is bad!

After the translation units are compiled separately, they are then linked to the final program. There are certain rules for multiple declarations between translation units. One of these rules (Β§3.2 / 4):

Each program must contain exactly one definition of each non-built-in function or variable that is odr-used in this program; no diagnostics required.

You have several myAwesomeFunction definitions in your program, and therefore you are breaking the rules. This is why your code is referencing incorrectly.

You can think of it from the point of view of the linker. After these two translation units are compiled, you have two object files. The task of the linker is to merge the object files together to form the final executable. Thus, he sees the call to myAwesomeFunction in main and tries to find the corresponding function definition in one of the object files. However, there are two definitions. Linker does not know which one to use, so that he simply gives up.

Now let's see what the translation units look like if you define myAwesomeFunction in something.cpp :

  • Fixed translation unit main.cpp :

     // Contents of <iostream> header here int myCoolFunction(); int myAwesomeFunction(); int main() { std::cout << myAwesomeFunction() << std::endl; } 
  • Fixed translation unit something.cpp :

     int myCoolFunction(); int myAwesomeFunction(); int myCoolFunction() { return 4; } int myAwesomeFunction() { return 3; } 

Now that’s fine. Currently, there is only one definition of myAwesomeFunction for the entire program. When the linker sees a call to myAwesomeFunction in main , he knows exactly in which function definition he must refer.

+3
source

The component simply tells you that you violated one rule of definition . This is a basic, well-documented C ++ rule - it cannot be solved using include guard or the #pragma once , but in the case of a free function, putting it inline or moving the implementation to the source file.

If a non-built-in method is implemented in the header, all translation units that include this header will define it. When the corresponding .obj files are linked to each other, the linker detects that the same character is exported several times (and defined) and complains.

Moving an implementation to a cpp file effectively converts your initial definition into a declaration.

+1
source

myAwesomeFunction defined in two source files: something.cpp and main.cpp . Move its implementation to one of the source files or declare this function as static.

-2
source

Source: https://habr.com/ru/post/1468951/


All Articles