In src1.cpp, I define a class names "Test" and a function names "f1" that creates a instance of Test.
// src1.cpp
#include <iostream>
class Test {
int a = 1;
public:
Test() {
std::cout << "src1 Test\n";
}
};
void f1() {
Test t;
}
In src1.h, "f1" is exposed.
// src1.h
#pragma once
void f1();
In much the same way, src2.cpp and src2.h is created. A class with the same name and a function that constructs an instance of it.
// src2.cpp
#include <iostream>
class Test {
long a;
public:
Test() {
std::cout << "src2 Test\n";
}
};
void f2() {
Test t;
}
// src2.h
#pragma once
void f2();
Then in the main.cc, I call both f1 and f2.
// main.cpp
#include "src1.h"
#include "src2.h"
int main() {
f1();
f2();
return 0;
}
I compile by the following command with no warning and error.
g++ -Wall -o main main.cpp src1.cpp src2.cpp
And the program output is:
src1 Test src1 Test
It seems compiler allow the different definition of the class Test and both f1 and f2 call the constructor of Test in src1.cpp.
And when I compile with the reverse order like
g++ -Wall -o main main.cpp src1.cpp src2.cpp
And the program output changes to:
src2 Test src2 Test
When I replace the class Test with a duplicate variable, the compile error occurs.
How does linker deal with the duplicate definitions in that case?
Your code is not allowed, even though you are getting no warning and error.
This code violates the one-definition-rule (ODR), because the definitions of
Testmust always be the same in different translation units (TUs) (i.e..cppfiles).However, odr-violations are a case of ill-formed, no diagnostic required (IFNDR). This means that your code is not valid C++, but the compiler is not required to issue any warning or error. C++ actually has a large amount of these IFNDR situations.
The wording in the C++ standard is this:
- [basic.def.odr]/14
This means that all definitions of
Tmust be completely identical, essentially copied/pasted versions of each other.Solution
It is very difficult to ensure that you don't violate the ODR manually. This is why you typically put types into headers, so they are guaranteed to be the same in all the cpp files which include them:
Alternatively, if you want to reuse the
Testname but give it different definitions:This also solves the problem, because the two
Testclasses have internal linkage, meaning that theTestina.cppandb.cpprefer to different types. Since they are different types, they can also be defined differently.Why can't the linker detect this, but detect duplicate variables?
The reason is simple: types aren't entities that get emitted like functions and variables, they just exist in the program. They still have to be defined the same everywhere, but the definition of
Testalone produces no assembly at all.Even if it id, it is difficult to detect an odr-violation, because
Testcan be defined in multiple places. It just needs to be defined the same way. The linker would have to somehow test for equality between two classes, but the C++ standard has very low requirements for linkers. You only get guaranteed diagnostics when using C++20 modules.See Also