Reputation: 642
This question builds off these two stackoverflow posts:
Here's the question: Why doesn't the multiple definition error appear for classes/structs/enums? Why does it only apply to functions or variables?
I wrote some example code in an effort to capture my confusion. There are 4 files: namespace.h, test.h, test.cpp, and main.cpp. The first file is included in both test.cpp and main.cpp, which leads to the multiple definition error if the correct lines are uncommented.
// namespace.h
#ifndef NAMESPACE_H
#define NAMESPACE_H
namespace NamespaceTest {
// 1. Function in namespace: must be declaration, not defintion
int test(); // GOOD
// int test() { // BAD
// return 5;
//}
// 2. Classes can live in header file with full implementation
// But if the function is defined outside of the struct, it causes compiler error
struct TestStruct {
int x;
int test() { return 10; } // GOOD
};
//int TestStruct::test() { // BAD
// return 10;
//}
// 3. Variables are also not spared from the multiple definition error.
//int x = 20; // BAD
// 4. But enums are perfectly safe.
enum TestEnum { ONE, TWO }; // GOOD
}
#endif
// test.h
#ifndef TEST_H
#define TEST_H
class Test {
public:
int test();
};
#endif
// test.cpp
#include "test.h"
#include "namespace.h"
int NamespaceTest::test() {
return 5;
}
int Test::test() {
return NamespaceTest::test() + 1;
}
// main.cpp
#include <iostream>
#include "namespace.h"
#include "test.h"
int main() {
std::cout << "NamespaceTest::test: " << NamespaceTest::test() << std::endl;
Test test;
std::cout << "Test::test: " <<test.test() << std::endl;
NamespaceTest::TestStruct test2;
std::cout << "NamespaceTest::TestStruct::test: " << test2.test() << std::endl;
std::cout << "NamespaceTest::x: " << NamespaceTest::TestEnum::ONE << std::endl;
}
g++ test.cpp main.cpp -o main.out && ./main.out
NamespaceTest::test: 5
Test::test: 6
NamespaceTest::TestStruct::test: 10
NamespaceTest::x: 0
Upvotes: 5
Views: 1984
Reputation: 11
my interpretation to
Why doesn't the multiple definition error appear for classes/structs/enums? Why does it only apply to functions or variables?
is, functions and variables have specific memory region allocated to them, and linker refers to this memory region by their symbol name. for example a function is a block of code in the .text region. or a variable may exist in .bss etc. Therefore, they shall not have multiple definition. But class/struct/enum definitions are just a layout, a plan, a definition of the organization of a block of memory.
Upvotes: 0
Reputation: 40811
Generally when you compile a definition that is namespace scoped (like functions or global variables), your compiler will emit a global symbol for it. If this appears in multiple translation units, there will be a conflict during link-time since there are multiple definitions (which happen to be equivalent, but the linker can't check this).
This is part of the one definition rule: Exactly one definition of a function or variable is allowed in the entire program, in one of the translation units.
There are some exceptions to this, for example, class definitions and inline functions/variables. However, definitions must be the exact same (textually) in all the translation units they appear in. Class definitions are meant to be #include
d, so it makes sense to allow them to appear in multiple translation units.
If you define a member function inside the class body they are implicitly inline
because otherwise you would not be able to include the class definition with the member function definition without breaking ODR. For example, these three are functionally equivalent:
struct TestStruct {
int x;
int test() { return 10; }
};
// Could have been written
struct TestStruct {
int x;
inline int test() { return 10; }
};
// Or as
struct TestStruct {
int x;
int test(); // The `inline` specifier could also be here
};
inline int TestStruct::test() { return 10; }
You can do this to your namespace scoped functions/variables too: inline int test() { return 5; }
and inline int x = 20;
would have compiled with no further issue.
This is implemented by the compiler emitting "specially marked" symbols for inline entities, and the linker picking one arbitrarily since they should all be the same.
The same exception to ODR also exists for templated functions / variables and enum declarations, since they are also meant to live in header files.
Upvotes: 2
Reputation: 642
After reading cppreference: inline specifier, I have a partial answer. The rules for inline stipulate that functions defined within classes are considered inline. And inline functions are permitted to have duplicate definitions provided (1) they live in separate translation units and (2) are identical. I'm paraphrasing, but that's the gist.
That explains why the functions are legal, but not why multiple definitions of the class or enum are ok. Likely a similar explanation I imagine, but it would be good to know for sure.
Upvotes: 2