skip to Main Content

I’m experiencing a weird linker issue in Visual Studio 2022 (C++). I have the following (CMake) project structure:

- src
   - a.cpp
   - main.cpp
- include
   - a.h
   - b.h
   - main.h
CMakeLists.txt
CMakePresets.json

Contents of files in the src directory:

a.cpp

#include "a.h"

// nothing besides the include

main.cpp

#include "main.h"

int main() {
    func();

    return 0;
}

Contents of files in the include directory:

a.h:

#pragma once

#include "b.h"

// nothing besides the include

b.h

#pragma once

void func() {
}

main.h

#pragma once

#include "a.h"

Compilation results in two errors:

  1. Error LNK2005: "void __cdecl func(void)" (?func@@YAXXZ) already defined in a.cpp.obj
  2. Fatal error LNK1169: one or more multiply defined symbols found

Not sure why, but It seems as though func is copied twice by the compiler due to the nested #include directives (e.g., main.h <- a.h <- b.h (func definition)). I’m able to solve the problem by placing func in an anonymous namespace like so:

b.h:

#pragma once

namespace {
    void func() {
    }
}

In other words, it appears that as long as func has external linkage the program will not compile. Even weirder, if I change func to be a template:

b.h:

#pragma once

template<typename T>
void func(T a) {
}

(and, correspondingly, modify the call to func(something) in main.cpp), the code compiles without problem! Even though a function generated from function template is supposed to have external linkage as well. What is going on here?

2

Answers


  1. An #include preprocessor instruction extends the content of the included file into the file being processed before the compiler compiles the file:

    • main.cpp includes main.h which includes a.h which includes b.h where func is defined (as opposed to only declared).
      To the compiler (i.e. post-preprocessor execution), main.cpp looks like:

       namespace {
           void func() {
           }
       }
       int main() {
           func();
      
           return 0;
       }
      

      The func symbol gets created during compilation into main.obj.

    • a.cpp includes a.h which also includes b.h where func, like
      previously, is defined.
      Like above, to the compiler, a.cpp looks like:

       namespace {
           void func() {
           }
       }
      

    Consequence:

    When the linker executes, it sees func in both main.obj and a.obj.

    Login or Signup to reply.
  2. It’s because both translation units (the one built from a.cpp and the one built from main.cpp) includes a definition of func which violates the one definition rule.

    The solution is to only declare func in b.h:

    #pragma once
    
    void func();
    

    and define it in b.cpp:

    #include "b.h"
    
    void func() {
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search