9
votes

If I have a file foo.cpp with the following code:

class Foo {
};

class Foo {
};

int main() {
    return 0;
}

Then naturally I get error: redefinition of 'Foo'. However, if I have foo.cpp with

class Foo {
};

int main() {
    return 0;
}

And bar.cpp with

class Foo {
};

Despite class Foo being defined twice across the program, this whole thing compiles fine.

If I had put int something; in both files in global namespace, then I would've gotten a linker error (specifically duplicate symbol), but for class definitions, this never happens.

I know function declarations such as int doIt(); can be duplicated in both cpp files, but a definition, e.g. int doIt() {} cannot be. Now in the first compiler error (with class Foo{}; twice in one cpp file), it said redefinition of foo, so class Foo{}; is a definition. Then why, unlike functions, can it be defined twice in one program?

EDIT: According to this website, named classes have external linkage. So why then is there no clash between class Foo across both cpp files?

EDIT2: According to the website linked above, not only do named classes have external linkage, but so do it's static members. Yet this all compiles fine:

foo.cpp:

class Foo {
public:
    int foo();
    static int x;
};

int Foo::foo() {
    return 5;
}

int main() {
    return 0;
}

bar.cpp:

class Foo {
public:
    int foo(int);
    static bool x;
};

int Foo::foo(int i) {
    return i * 2;
}

Not only has Foo::foo been redefined with a different signature, but Foo::x is of a different type. Both of these should have external linkage yet this code is A-ok.

4
what compiler is that?ΦXocę 웃 Пepeúpa ツ
This question has been discussed here, tl;dr; When classes (enums, unions, etc.) with same name are separately defined in different translation units they have internal or no linkage so no error occurs. Note, that accepted answer there is wrong and the one with more upvotes is correct.user7860670
@ΦXocę웃Пepeúpaツ Apple LLVM version 7.3.0 (clang-703.0.31)rcplusplus
Are you interested in the corresponding rule(s) of the standard, or the motivation for that?chtz
@VTT You linked to the right question, pointed to the right answer, and then summarized it all wrong.T.C.

4 Answers

3
votes

Regarding your first question, with identical definitions across multiple TU's, that's explicitly allowed by ODR, because otherwise the language would be useless.

Regarding the second question, with different definitions in different TU's, that is an ODR-violation. However, those are NDR. Your program is still malformed, and it will probably cause weird errors.

Regarding the third question, with static data members, those are declarations, not definitions. They need an unique definition, like:

TheType ClassName::VariableName;

Those are typically placed in the accompanying .cpp file.

There is an exception to that, with const static data members with inline initializers.


ODR = One Definition Rule
TU = Translation Unit
NDR = No Diagnostic Required

A note regarding NDR; some kind of errors are hard for the compiler to detect, and the standard usually do not require that the compiler issue a diagnostic (ie warning or error) in those cases. There are tools, such as CppLint, that can detect many of the errors the compiler can not. When it comes to ODR-violations, those can usually be avoided by only define types in headers.

1
votes

Because of "One Definition Rule" in C++. You can't redefine class within one translation unit, but class can (and should be) defined in each translation unit which uses it. That's why headers and #include exist in C/C++. You should place class definition in header and include it to each .cpp which uses it. It prevents ODR violation but technically using #include is the same as definition the class in each .cpp file (preprocessor just makes the included file a part of compiled file).

Also pay attention to how definition differs from declaration in C++.

Upd. In your new example with static member variables you have only declarations without definition:

class Foo {
public:
    static int x; // <-- variable declaration
};
int Foo::x; // <-- variable definition

One can duplicate declarations within translation unit but not definitions.

Definition of types (including classes) can be duplicated in different translation units, functions and variables with external linkage - not.

Definition of two types in two translation units with the same name but different structure is ODR violation which linkers usually can't diagnose - your program is incorrect but all "builds just fine".

Translation unit is what the compiler get as an input after preprocessing. Using clang or gcc you can get it like this:

$ clang -E foo.cpp >foo.ii
0
votes

Each cpp file compile all definitions independently. Their is no difference from the compiler's perspective from defining it twice in two different cpp files, or once in a shared include.

A class definition in and of it self doesn't introduce any user defined symbols, and thus will not produce a linker error. Only class methods and static members do. If the methods are defined within the class definition they are treated as inline. Inline functions are marked such that the linker will select any available definition and assume it is equivalent to all others. If these methods are not defined within the class definition and not marked inline, multiple instances will result in a linker error.

-1
votes

Normally classes are defined in header files and if you do such then you would get errors when including the header file.