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:
Error LNK2005: "void __cdecl func(void)" (?func@@YAXXZ) already defined in a.cpp.obj
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
An
#include
preprocessor instruction extends the content of the included file into the file being processed before the compiler compiles the file:main.cpp
includesmain.h
which includesa.h
which includesb.h
wherefunc
is defined (as opposed to only declared).To the compiler (i.e. post-preprocessor execution),
main.cpp
looks like:The
func
symbol gets created during compilation intomain.obj
.a.cpp
includesa.h
which also includesb.h
wherefunc
, likepreviously, is defined.
Like above, to the compiler,
a.cpp
looks like:Consequence:
When the linker executes, it sees
func
in bothmain.obj
anda.obj
.It’s because both translation units (the one built from
a.cpp
and the one built frommain.cpp
) includes a definition offunc
which violates the one definition rule.The solution is to only declare
func
inb.h
:and define it in
b.cpp
: