9

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.

rcplusplus
  • 2,767
  • 5
  • 29
  • 43
  • what compiler is that? – ΦXocę 웃 Пepeúpa ツ Apr 18 '17 at 04:12
  • 3
    This question [has been discussed here](http://stackoverflow.com/questions/6465325/do-classes-have-external-linkage), 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 Apr 18 '17 at 04:20
  • @ΦXocę웃Пepeúpaツ Apple LLVM version 7.3.0 (clang-703.0.31) – rcplusplus Apr 18 '17 at 04:26
  • Are you interested in the corresponding rule(s) of the standard, or the motivation for that? – chtz Apr 18 '17 at 06:08
  • 3
    @VTT You linked to the right question, pointed to the right answer, and then summarized it all wrong. – T.C. Apr 18 '17 at 06:45
  • @chtz both really. the more I learn the better – rcplusplus Apr 18 '17 at 15:58

4 Answers4

3

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.

John Smith
  • 169
  • 1
  • 4
1

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
Victor Dyachenko
  • 1,363
  • 8
  • 18
  • definition and declaration I'm comfortable with, it's just that class definition isn't what I think it is for some reason. According to ODR, redefining a class across multiple files is ok _only if_ the definition remains the same. Yet in my edits, I showed code that (I believe) violates this. – rcplusplus Apr 18 '17 at 20:09
  • @rcplusplus Yeah, you can write code that violates the Standard and get complete silence from the compiler, if the Standard does not mandate a diagnostic and the compiler chose not to report one. That doesn't make the code any less broken. – underscore_d Apr 19 '17 at 09:11
0

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.

nate
  • 1,771
  • 12
  • 17
  • The last sentence is not completely correct, since you can manually mark functions as `inline` and implement them outside the class body. – chtz Apr 18 '17 at 16:03
-1

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

  • 4
    That does not answer the question. – Jesper Juhl Apr 18 '17 at 05:53
  • 1
    It sort of answers the question : if this produced a linker error than it would be impossible to use classes in a project with multiple units, therefore it cannot produce an error! – M.M Apr 18 '17 at 14:20
  • I knew that header files do this which is why I asked. If you put a variable in a header file you get multiple definition error, yet not with a class. – rcplusplus Apr 18 '17 at 15:59
  • 1
    @rcplusplus Definition of variable creates object, definition of function creates code - all are (named) linker objects. But class/type definition doesn't generate linker object so you don't have names clash during linking. But definition of the class must be the same in all linked object files – Victor Dyachenko Apr 18 '17 at 19:25
  • @VictorDyachenko But putting `class Foo { int x; };` in `foo.cpp` and `class Foo { bool x; }` in `bar.cpp` and compiling them together works just fine. – rcplusplus Apr 18 '17 at 19:36
  • 1
    @rcplusplus I don't think that it "works fine". The objects have different binary structure. Linker is just unable to detect ODR violation - it operates only with "symbols" i.e. names – Victor Dyachenko Apr 19 '17 at 05:55